Skip to content

Commit

Permalink
Tailwind-compatible flex section: round 1 (#6650)
Browse files Browse the repository at this point in the history
## 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
  • Loading branch information
bkrmendy authored Nov 19, 2024
1 parent 9902478 commit bcde12d
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 19 deletions.
16 changes: 13 additions & 3 deletions editor/src/components/canvas/plugins/tailwind-style-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,30 @@ function parseTailwindProperty<T>(
}

const TailwindPropertyMapping: Record<string, string> = {
gap: 'gap',
flexDirection: 'flexDirection',
left: 'positionLeft',
right: 'positionRight',
top: 'positionTop',
bottom: 'positionBottom',

width: 'width',
height: 'height',
flexBasis: 'flexBasis',

padding: 'padding',
paddingTop: 'paddingTop',
paddingRight: 'paddingRight',
paddingBottom: 'paddingBottom',
paddingLeft: 'paddingLeft',

justifyContent: 'justifyContent',
alignItems: 'alignItems',
flex: 'flex',
flexDirection: 'flexDirection',
flexGrow: 'flexGrow',
flexShrink: 'flexShrink',
flexBasis: 'flexBasis',
flexWrap: 'flexWrap',
gap: 'gap',

zIndex: 'zIndex',
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import {
expectSingleUndo2Saves,
hoverControlWithCheck,
selectComponentsForTest,
setFeatureForBrowserTestsUseInDescribeBlockOnly,
} from '../../utils/utils.test-utils'
import { CanvasControlsContainerID } from '../canvas/controls/new-canvas-controls'
import { getSubduedPaddingControlTestID } from '../canvas/controls/select-mode/subdued-padding-control'
import { mouseClickAtPoint } from '../canvas/event-helpers.test-utils'
import type { EditorRenderResult } from '../canvas/ui-jsx.test-utils'
import { renderTestEditorWithCode } from '../canvas/ui-jsx.test-utils'
import { renderTestEditorWithCode, renderTestEditorWithModel } from '../canvas/ui-jsx.test-utils'
import type { FlexDirection } from './common/css-utils'
import { FlexDirectionControlTestId, FlexDirectionToggleTestId } from './flex-direction-control'
import { TailwindProject } from './sections/flex-section.test-utils'

describe('set flex direction', () => {
it('set flex direction to row from not set', async () => {
Expand Down Expand Up @@ -111,6 +113,62 @@ describe('set flex direction', () => {
expect(controls.length).toEqual(4)
})
})

describe('Tailwind', () => {
setFeatureForBrowserTestsUseInDescribeBlockOnly('Tailwind', true)

it('set flex direction to column from not set', async () => {
const editor = await renderTestEditorWithModel(
TailwindProject('flex'),
'await-first-dom-report',
)
await selectDiv(editor)
await expectSingleUndo2Saves(editor, () => clickOn(editor, 'column'))
const div = editor.renderedDOM.getByTestId('mydiv')
expect(div.className).toEqual(
'top-10 left-10 w-64 h-64 bg-slate-100 absolute flex flex-col', // flex-col is set by the control
)
})

it('set flex direction to row-reverse from not set', async () => {
const editor = await renderTestEditorWithModel(
TailwindProject('flex'),
'await-first-dom-report',
)
await selectDiv(editor)
await expectSingleUndo2Saves(editor, () => clickOn(editor, 'row-reverse'))
const div = editor.renderedDOM.getByTestId('mydiv')
expect(div.className).toEqual(
'top-10 left-10 w-64 h-64 bg-slate-100 absolute flex flex-row-reverse', // flex-row-reverse is set by the control
)
})

it('set flex direction to column-reverse from not set', async () => {
const editor = await renderTestEditorWithModel(
TailwindProject('flex'),
'await-first-dom-report',
)
await selectDiv(editor)
await expectSingleUndo2Saves(editor, () => clickOn(editor, 'column-reverse'))
const div = editor.renderedDOM.getByTestId('mydiv')
expect(div.className).toEqual(
'top-10 left-10 w-64 h-64 bg-slate-100 absolute flex flex-col-reverse', // flex-row-reverse is set by the control
)
})

it('set flex direction to row from column', async () => {
const editor = await renderTestEditorWithModel(
TailwindProject('flex flex-col'),
'await-first-dom-report',
)
await selectDiv(editor)
await expectSingleUndo2Saves(editor, () => clickOn(editor, 'row'))
const div = editor.renderedDOM.getByTestId('mydiv')
expect(div.className).toEqual(
'top-10 left-10 w-64 h-64 bg-slate-100 absolute flex flex-row', // flex-row is set by the control
)
})
})
})

async function selectDiv(editor: EditorRenderResult): Promise<HTMLElement> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@ import {
expectSingleUndo2Saves,
hoverControlWithCheck,
selectComponentsForTest,
setFeatureForBrowserTestsUseInDescribeBlockOnly,
} from '../../utils/utils.test-utils'
import { CanvasControlsContainerID } from '../canvas/controls/new-canvas-controls'
import { getSubduedPaddingControlTestID } from '../canvas/controls/select-mode/subdued-padding-control'
import { mouseClickAtPoint } from '../canvas/event-helpers.test-utils'
import type { EditorRenderResult } from '../canvas/ui-jsx.test-utils'
import { renderTestEditorWithCode } from '../canvas/ui-jsx.test-utils'
import { renderTestEditorWithCode, renderTestEditorWithModel } from '../canvas/ui-jsx.test-utils'
import type { StartCenterEnd } from './inspector-common'
import { NineBlockControlTestId, NineBlockSectors, NineBlockTestId } from './nine-block-controls'
import {
AlignItemsClassMapping,
JustifyContentClassMapping,
TailwindProject,
} from './sections/flex-section.test-utils'

describe('Nine-block control', () => {
describe('in flex row', () => {
Expand Down Expand Up @@ -49,6 +55,27 @@ describe('Nine-block control', () => {
expect(controls.length).toEqual(4)
})
})

describe('Tailwind', () => {
setFeatureForBrowserTestsUseInDescribeBlockOnly('Tailwind', true)

for (const [justifyContent, alignItems] of NineBlockSectors) {
it(`set ${justifyContent} and ${alignItems} via the nine-block control`, async () => {
const editor = await renderTestEditorWithModel(
TailwindProject('flex flex-row'),
'await-first-dom-report',
)

const div = await doTest(editor, alignItems, justifyContent)

expect(getComputedStyle(div).justifyContent).toEqual(justifyContent)
expect(getComputedStyle(div).alignItems).toEqual(alignItems)
expect(div.className).toEqual(
`top-10 left-10 w-64 h-64 bg-slate-100 absolute flex flex-row ${AlignItemsClassMapping[alignItems]} ${JustifyContentClassMapping[justifyContent]}`,
)
})
}
})
})

async function doTest(
Expand All @@ -60,8 +87,8 @@ async function doTest(
const div = editor.renderedDOM.getByTestId('mydiv')
const divBounds = div.getBoundingClientRect()
const divCorner = {
x: divBounds.x + 50,
y: divBounds.y + 40,
x: divBounds.x + 5,
y: divBounds.y + 4,
}

await mouseClickAtPoint(canvasControlsLayer, divCorner)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { TailwindConfigPath } from '../../../core/tailwind/tailwind-config'
import { createModifiedProject } from '../../../sample-projects/sample-project-utils.test-utils'
import { StoryboardFilePath } from '../../editor/store/editor-state'

export const JustifyContentClassMapping = {
'flex-start': 'justify-start',
center: 'justify-center',
'flex-end': 'justify-end',
} as const

export const AlignItemsClassMapping = {
'flex-start': 'items-start',
center: 'items-center',
'flex-end': 'items-end',
} as const

export const TailwindProject = (classes: string) =>
createModifiedProject({
[StoryboardFilePath]: `
import React from 'react'
import { Scene, Storyboard } from 'utopia-api'
export var storyboard = (
<Storyboard data-uid='sb'>
<Scene
id='scene'
commentId='scene'
data-uid='scene'
style={{
width: 700,
height: 759,
position: 'absolute',
left: 212,
top: 128,
}}
>
<div
data-uid='mydiv'
data-testid='mydiv'
className='top-10 left-10 w-64 h-64 bg-slate-100 absolute ${classes}'
>
<div className='bg-red-500 w-10 h-10' data-uid='child-1' />
<div className='bg-red-500 w-10 h-10' data-uid='child-2' />
</div>
</Scene>
</Storyboard>
)
`,
[TailwindConfigPath]: `
const TailwindConfig = { }
export default TailwindConfig
`,
'app.css': `
@tailwind base;
@tailwind components;
@tailwind utilities;`,
})
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import {
expectSingleUndo2Saves,
hoverControlWithCheck,
selectComponentsForTest,
setFeatureForBrowserTestsUseInDescribeBlockOnly,
} from '../../utils/utils.test-utils'
import { getSubduedPaddingControlTestID } from '../canvas/controls/select-mode/subdued-padding-control'
import { mouseClickAtPoint } from '../canvas/event-helpers.test-utils'
import type { EditorRenderResult } from '../canvas/ui-jsx.test-utils'
import { renderTestEditorWithCode } from '../canvas/ui-jsx.test-utils'
import { renderTestEditorWithCode, renderTestEditorWithModel } from '../canvas/ui-jsx.test-utils'
import { TailwindProject } from './sections/flex-section.test-utils'
import {
PackedLabelCopy,
SpacedLabelCopy,
Expand Down Expand Up @@ -46,6 +48,35 @@ describe('spaced - packed control', () => {
expect(div.style.justifyContent).toEqual('flex-start')
})

describe('Tailwind', () => {
setFeatureForBrowserTestsUseInDescribeBlockOnly('Tailwind', true)
it('set element to spaced layout', async () => {
const editor = await renderTestEditorWithModel(
TailwindProject('flex flex-row'),
'await-first-dom-report',
)
await selectComponentsForTest(editor, [EP.fromString('sb/scene/mydiv')])
await expectSingleUndo2Saves(editor, () => clickButton(editor, SpacedLabelCopy))
const div = editor.renderedDOM.getByTestId('mydiv')
expect(div.className).toEqual(
'top-10 left-10 w-64 h-64 bg-slate-100 absolute flex flex-row justify-between',
)
})
it('set element to packed layout', async () => {
const editor = await renderTestEditorWithModel(
TailwindProject('flex flex-row'),
'await-first-dom-report',
)
await selectComponentsForTest(editor, [EP.fromString('sb/scene/mydiv')])
await expectSingleUndo2Saves(editor, () => clickButton(editor, SpacedLabelCopy))
await expectSingleUndo2Saves(editor, () => clickButton(editor, PackedLabelCopy))
const div = editor.renderedDOM.getByTestId('mydiv')
expect(div.className).toEqual(
'top-10 left-10 w-64 h-64 bg-slate-100 absolute flex flex-row justify-start',
)
})
})

it('when spaced/packed control is hovered, padding hihglights are shown', async () => {
const editor = await renderTestEditorWithCode(projectWithPadding, 'await-first-dom-report')
await selectComponentsForTest(editor, [EP.fromString('sb/div')])
Expand Down
Loading

0 comments on commit bcde12d

Please sign in to comment.