Skip to content

Commit bcde12d

Browse files
authored
Tailwind-compatible flex section: round 1 (#6650)
## Problem The controls to set `justify-contents`, `align-items` and `flex-direction` don't work with Tailwind ## Fix - Add the necessary class names to `TailwindClassNameMapping`, so that the Tailwind style plugin can write these classes. These controls read props from `specialSizeMeasurements`, so I'm leaving `StyleInfo` unchanged in this PR, and add the flex-related classes I added to `TailwindClassNameMapping` when something actually needs to read them through `StyleInfo`. - Add tests for Tailwind editing in the affected controls ### Commit Details - Add the necessary class names to `TailwindClassNameMapping` - Add tests for `NineBlockControl` (used to set `justify-contents` and `align-items`) - Add tests for `ThreebarControl` (used to set `align-items` when `justify-contents` is set to `space-between`) - Add tests for `SpacedPackedControls` (sets `justify-contents` to either `space-between` for spaced or to `flex-start` for `packed`) - Add tests for `FlexDirectionToggle` (used to set flex direction) ### Out of scope There are two other controls in the flex section, used for setting `flex-wrap` and `flex-gap`. These controls use `useInspectorInfo` under the hood, making that work is left to a follow-up PR ## Manual Tests I hereby swear that: - [x] I opened a hydrogen project and it loaded - [x] I could navigate to various routes in Play mode
1 parent 9902478 commit bcde12d

6 files changed

+274
-19
lines changed

editor/src/components/canvas/plugins/tailwind-style-plugin.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,30 @@ function parseTailwindProperty<T>(
2323
}
2424

2525
const TailwindPropertyMapping: Record<string, string> = {
26-
gap: 'gap',
27-
flexDirection: 'flexDirection',
2826
left: 'positionLeft',
2927
right: 'positionRight',
3028
top: 'positionTop',
3129
bottom: 'positionBottom',
30+
3231
width: 'width',
3332
height: 'height',
34-
flexBasis: 'flexBasis',
33+
3534
padding: 'padding',
3635
paddingTop: 'paddingTop',
3736
paddingRight: 'paddingRight',
3837
paddingBottom: 'paddingBottom',
3938
paddingLeft: 'paddingLeft',
39+
40+
justifyContent: 'justifyContent',
41+
alignItems: 'alignItems',
42+
flex: 'flex',
43+
flexDirection: 'flexDirection',
44+
flexGrow: 'flexGrow',
45+
flexShrink: 'flexShrink',
46+
flexBasis: 'flexBasis',
47+
flexWrap: 'flexWrap',
48+
gap: 'gap',
49+
4050
zIndex: 'zIndex',
4151
}
4252

editor/src/components/inspector/flex-direction-control.spec.browser2.tsx

+59-1
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ import {
33
expectSingleUndo2Saves,
44
hoverControlWithCheck,
55
selectComponentsForTest,
6+
setFeatureForBrowserTestsUseInDescribeBlockOnly,
67
} from '../../utils/utils.test-utils'
78
import { CanvasControlsContainerID } from '../canvas/controls/new-canvas-controls'
89
import { getSubduedPaddingControlTestID } from '../canvas/controls/select-mode/subdued-padding-control'
910
import { mouseClickAtPoint } from '../canvas/event-helpers.test-utils'
1011
import type { EditorRenderResult } from '../canvas/ui-jsx.test-utils'
11-
import { renderTestEditorWithCode } from '../canvas/ui-jsx.test-utils'
12+
import { renderTestEditorWithCode, renderTestEditorWithModel } from '../canvas/ui-jsx.test-utils'
1213
import type { FlexDirection } from './common/css-utils'
1314
import { FlexDirectionControlTestId, FlexDirectionToggleTestId } from './flex-direction-control'
15+
import { TailwindProject } from './sections/flex-section.test-utils'
1416

1517
describe('set flex direction', () => {
1618
it('set flex direction to row from not set', async () => {
@@ -111,6 +113,62 @@ describe('set flex direction', () => {
111113
expect(controls.length).toEqual(4)
112114
})
113115
})
116+
117+
describe('Tailwind', () => {
118+
setFeatureForBrowserTestsUseInDescribeBlockOnly('Tailwind', true)
119+
120+
it('set flex direction to column from not set', async () => {
121+
const editor = await renderTestEditorWithModel(
122+
TailwindProject('flex'),
123+
'await-first-dom-report',
124+
)
125+
await selectDiv(editor)
126+
await expectSingleUndo2Saves(editor, () => clickOn(editor, 'column'))
127+
const div = editor.renderedDOM.getByTestId('mydiv')
128+
expect(div.className).toEqual(
129+
'top-10 left-10 w-64 h-64 bg-slate-100 absolute flex flex-col', // flex-col is set by the control
130+
)
131+
})
132+
133+
it('set flex direction to row-reverse from not set', async () => {
134+
const editor = await renderTestEditorWithModel(
135+
TailwindProject('flex'),
136+
'await-first-dom-report',
137+
)
138+
await selectDiv(editor)
139+
await expectSingleUndo2Saves(editor, () => clickOn(editor, 'row-reverse'))
140+
const div = editor.renderedDOM.getByTestId('mydiv')
141+
expect(div.className).toEqual(
142+
'top-10 left-10 w-64 h-64 bg-slate-100 absolute flex flex-row-reverse', // flex-row-reverse is set by the control
143+
)
144+
})
145+
146+
it('set flex direction to column-reverse from not set', async () => {
147+
const editor = await renderTestEditorWithModel(
148+
TailwindProject('flex'),
149+
'await-first-dom-report',
150+
)
151+
await selectDiv(editor)
152+
await expectSingleUndo2Saves(editor, () => clickOn(editor, 'column-reverse'))
153+
const div = editor.renderedDOM.getByTestId('mydiv')
154+
expect(div.className).toEqual(
155+
'top-10 left-10 w-64 h-64 bg-slate-100 absolute flex flex-col-reverse', // flex-row-reverse is set by the control
156+
)
157+
})
158+
159+
it('set flex direction to row from column', async () => {
160+
const editor = await renderTestEditorWithModel(
161+
TailwindProject('flex flex-col'),
162+
'await-first-dom-report',
163+
)
164+
await selectDiv(editor)
165+
await expectSingleUndo2Saves(editor, () => clickOn(editor, 'row'))
166+
const div = editor.renderedDOM.getByTestId('mydiv')
167+
expect(div.className).toEqual(
168+
'top-10 left-10 w-64 h-64 bg-slate-100 absolute flex flex-row', // flex-row is set by the control
169+
)
170+
})
171+
})
114172
})
115173

116174
async function selectDiv(editor: EditorRenderResult): Promise<HTMLElement> {

editor/src/components/inspector/nine-block-control.spec.browser2.tsx

+30-3
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@ import {
33
expectSingleUndo2Saves,
44
hoverControlWithCheck,
55
selectComponentsForTest,
6+
setFeatureForBrowserTestsUseInDescribeBlockOnly,
67
} from '../../utils/utils.test-utils'
78
import { CanvasControlsContainerID } from '../canvas/controls/new-canvas-controls'
89
import { getSubduedPaddingControlTestID } from '../canvas/controls/select-mode/subdued-padding-control'
910
import { mouseClickAtPoint } from '../canvas/event-helpers.test-utils'
1011
import type { EditorRenderResult } from '../canvas/ui-jsx.test-utils'
11-
import { renderTestEditorWithCode } from '../canvas/ui-jsx.test-utils'
12+
import { renderTestEditorWithCode, renderTestEditorWithModel } from '../canvas/ui-jsx.test-utils'
1213
import type { StartCenterEnd } from './inspector-common'
1314
import { NineBlockControlTestId, NineBlockSectors, NineBlockTestId } from './nine-block-controls'
15+
import {
16+
AlignItemsClassMapping,
17+
JustifyContentClassMapping,
18+
TailwindProject,
19+
} from './sections/flex-section.test-utils'
1420

1521
describe('Nine-block control', () => {
1622
describe('in flex row', () => {
@@ -49,6 +55,27 @@ describe('Nine-block control', () => {
4955
expect(controls.length).toEqual(4)
5056
})
5157
})
58+
59+
describe('Tailwind', () => {
60+
setFeatureForBrowserTestsUseInDescribeBlockOnly('Tailwind', true)
61+
62+
for (const [justifyContent, alignItems] of NineBlockSectors) {
63+
it(`set ${justifyContent} and ${alignItems} via the nine-block control`, async () => {
64+
const editor = await renderTestEditorWithModel(
65+
TailwindProject('flex flex-row'),
66+
'await-first-dom-report',
67+
)
68+
69+
const div = await doTest(editor, alignItems, justifyContent)
70+
71+
expect(getComputedStyle(div).justifyContent).toEqual(justifyContent)
72+
expect(getComputedStyle(div).alignItems).toEqual(alignItems)
73+
expect(div.className).toEqual(
74+
`top-10 left-10 w-64 h-64 bg-slate-100 absolute flex flex-row ${AlignItemsClassMapping[alignItems]} ${JustifyContentClassMapping[justifyContent]}`,
75+
)
76+
})
77+
}
78+
})
5279
})
5380

5481
async function doTest(
@@ -60,8 +87,8 @@ async function doTest(
6087
const div = editor.renderedDOM.getByTestId('mydiv')
6188
const divBounds = div.getBoundingClientRect()
6289
const divCorner = {
63-
x: divBounds.x + 50,
64-
y: divBounds.y + 40,
90+
x: divBounds.x + 5,
91+
y: divBounds.y + 4,
6592
}
6693

6794
await mouseClickAtPoint(canvasControlsLayer, divCorner)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { TailwindConfigPath } from '../../../core/tailwind/tailwind-config'
2+
import { createModifiedProject } from '../../../sample-projects/sample-project-utils.test-utils'
3+
import { StoryboardFilePath } from '../../editor/store/editor-state'
4+
5+
export const JustifyContentClassMapping = {
6+
'flex-start': 'justify-start',
7+
center: 'justify-center',
8+
'flex-end': 'justify-end',
9+
} as const
10+
11+
export const AlignItemsClassMapping = {
12+
'flex-start': 'items-start',
13+
center: 'items-center',
14+
'flex-end': 'items-end',
15+
} as const
16+
17+
export const TailwindProject = (classes: string) =>
18+
createModifiedProject({
19+
[StoryboardFilePath]: `
20+
import React from 'react'
21+
import { Scene, Storyboard } from 'utopia-api'
22+
export var storyboard = (
23+
<Storyboard data-uid='sb'>
24+
<Scene
25+
id='scene'
26+
commentId='scene'
27+
data-uid='scene'
28+
style={{
29+
width: 700,
30+
height: 759,
31+
position: 'absolute',
32+
left: 212,
33+
top: 128,
34+
}}
35+
>
36+
<div
37+
data-uid='mydiv'
38+
data-testid='mydiv'
39+
className='top-10 left-10 w-64 h-64 bg-slate-100 absolute ${classes}'
40+
>
41+
<div className='bg-red-500 w-10 h-10' data-uid='child-1' />
42+
<div className='bg-red-500 w-10 h-10' data-uid='child-2' />
43+
</div>
44+
</Scene>
45+
</Storyboard>
46+
)
47+
48+
`,
49+
[TailwindConfigPath]: `
50+
const TailwindConfig = { }
51+
export default TailwindConfig
52+
`,
53+
'app.css': `
54+
@tailwind base;
55+
@tailwind components;
56+
@tailwind utilities;`,
57+
})

editor/src/components/inspector/spaced-packed-control.spec.browser2.tsx

+32-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import {
33
expectSingleUndo2Saves,
44
hoverControlWithCheck,
55
selectComponentsForTest,
6+
setFeatureForBrowserTestsUseInDescribeBlockOnly,
67
} from '../../utils/utils.test-utils'
78
import { getSubduedPaddingControlTestID } from '../canvas/controls/select-mode/subdued-padding-control'
89
import { mouseClickAtPoint } from '../canvas/event-helpers.test-utils'
910
import type { EditorRenderResult } from '../canvas/ui-jsx.test-utils'
10-
import { renderTestEditorWithCode } from '../canvas/ui-jsx.test-utils'
11+
import { renderTestEditorWithCode, renderTestEditorWithModel } from '../canvas/ui-jsx.test-utils'
12+
import { TailwindProject } from './sections/flex-section.test-utils'
1113
import {
1214
PackedLabelCopy,
1315
SpacedLabelCopy,
@@ -46,6 +48,35 @@ describe('spaced - packed control', () => {
4648
expect(div.style.justifyContent).toEqual('flex-start')
4749
})
4850

51+
describe('Tailwind', () => {
52+
setFeatureForBrowserTestsUseInDescribeBlockOnly('Tailwind', true)
53+
it('set element to spaced layout', async () => {
54+
const editor = await renderTestEditorWithModel(
55+
TailwindProject('flex flex-row'),
56+
'await-first-dom-report',
57+
)
58+
await selectComponentsForTest(editor, [EP.fromString('sb/scene/mydiv')])
59+
await expectSingleUndo2Saves(editor, () => clickButton(editor, SpacedLabelCopy))
60+
const div = editor.renderedDOM.getByTestId('mydiv')
61+
expect(div.className).toEqual(
62+
'top-10 left-10 w-64 h-64 bg-slate-100 absolute flex flex-row justify-between',
63+
)
64+
})
65+
it('set element to packed layout', async () => {
66+
const editor = await renderTestEditorWithModel(
67+
TailwindProject('flex flex-row'),
68+
'await-first-dom-report',
69+
)
70+
await selectComponentsForTest(editor, [EP.fromString('sb/scene/mydiv')])
71+
await expectSingleUndo2Saves(editor, () => clickButton(editor, SpacedLabelCopy))
72+
await expectSingleUndo2Saves(editor, () => clickButton(editor, PackedLabelCopy))
73+
const div = editor.renderedDOM.getByTestId('mydiv')
74+
expect(div.className).toEqual(
75+
'top-10 left-10 w-64 h-64 bg-slate-100 absolute flex flex-row justify-start',
76+
)
77+
})
78+
})
79+
4980
it('when spaced/packed control is hovered, padding hihglights are shown', async () => {
5081
const editor = await renderTestEditorWithCode(projectWithPadding, 'await-first-dom-report')
5182
await selectComponentsForTest(editor, [EP.fromString('sb/div')])

0 commit comments

Comments
 (0)