[fix] various text (#1350)

This PR updates tests for the text shape, as well as updating the logic
of `getTextLines`. We now:

- allow leading whitespace
- allow white space to cause line breaks, trim the whitespace off of the
end of the line. Crazy times!
- fix a bug with geo shapes changes width when growY changes

Note that this is not a "full solution" to line breaks that are caused
by whitespace + wrapping. AFAIK this is impossible to fix in SVG-land
without measuring the SVG element in order to collapse whitespace in the
same way that it collapses in HTML layout.

### Change Type

- [x] `patch` — Bug Fix
- [ ] `minor` — New Feature
- [ ] `major` — Breaking Change

- [ ] `dependencies` — Dependency Update (publishes a `patch` release,
for devDependencies use `internal`)

- [ ] `documentation` — Changes to the documentation only (will not
publish a new version)
- [ ] `tests` — Changes to any testing-related code only (will not
publish a new version)
- [ ] `internal` — Any other changes that don't affect the published
package (will not publish a new version)

### Test Plan

- [x] Webdriver tests

### Release Notes

- Allow leading whitespace
This commit is contained in:
Steve Ruiz 2023-05-11 23:15:24 +01:00 committed by GitHub
parent 3437ca89d9
commit a722e3e6f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 78 additions and 21 deletions

View file

@ -20,4 +20,7 @@
**/setupJest.js
apps/webdriver/www
apps/vscode/extension/editor
apps/examples/www
apps/examples/www
apps/docs/content.json
apps/vscode/extension/editor/index.js
apps/vscode/extension/editor/tldraw-assets.json

View file

@ -8,4 +8,8 @@
**/.tsbuild*
**/.next/*
*.mdx
**/_archive/*
**/_archive/*
apps/docs/content.json
apps/vscode/extension/editor/index.js
apps/vscode/extension/editor/tldraw-assets.json
apps/webdriver/www/index.js

View file

@ -179,7 +179,7 @@ describe('text measurement', () => {
expect(lines).toEqual(['testing', 'testing'])
})
it('should strip whitespace at the end of unwrapped lines', async () => {
it('strips whitespace at the end of unwrapped lines', async () => {
await ui.app.setup()
const lines = await browser.execute((options) => {
return window.app.textMeasure.getTextLines({
@ -192,7 +192,7 @@ describe('text measurement', () => {
expect(lines).toEqual(['testing testing'])
})
it('should strip whitespace from the start of an unwrapped line', async () => {
it('preserves whitespace at the start of an unwrapped line', async () => {
await ui.app.setup()
const lines = await browser.execute((options) => {
return window.app.textMeasure.getTextLines({
@ -202,7 +202,7 @@ describe('text measurement', () => {
})
}, getTextLinesOptions)
expect(lines).toEqual(['testing testing'])
expect(lines).toEqual([' testing testing'])
})
it('should place starting whitespace on its own line if it has to', async () => {
@ -217,7 +217,43 @@ describe('text measurement', () => {
expect(lines).toEqual(['', 'testing', 'testing'])
})
it('should place ending whitespace on its own line if it has to', async () => {
it('trims ending whitespace', async () => {
await ui.app.setup()
const lines = await browser.execute((options) => {
return window.app.textMeasure.getTextLines({
...options,
text: 'testing testing ',
})
}, getTextLinesOptions)
expect(lines).toEqual(['testing', 'testing'])
})
it('allows whitespace to cause breaks, however trims it at the end anyway', async () => {
await ui.app.setup()
const lines = await browser.execute((options) => {
return window.app.textMeasure.getTextLines({
...options,
text: 'ok hi testing',
})
}, getTextLinesOptions)
expect(lines).toEqual(['ok hi', 'testing'])
})
it('respects leading whitespace', async () => {
await ui.app.setup()
const lines = await browser.execute((options) => {
return window.app.textMeasure.getTextLines({
...options,
text: ' ok hi testing ',
})
}, getTextLinesOptions)
expect(lines).toEqual([' ok hi', 'testing'])
})
it('should handle multiline text', async () => {
await ui.app.setup()
const lines = await browser.execute((options) => {
return window.app.textMeasure.getTextLines({
@ -228,6 +264,19 @@ describe('text measurement', () => {
expect(lines).toEqual(['testing', 'testing'])
})
it('should break long strings of text', async () => {
await ui.app.setup()
const lines = await browser.execute((options) => {
return window.app.textMeasure.getTextLines({
...options,
text: 'testingtestingtestingtestingtestingtesting',
})
}, getTextLinesOptions)
expect(lines).toEqual(['testingt', 'estingte', 'stingtes', 'tingtest', 'ingtesti', 'ng'])
})
it('should return an empty array if the text is empty', async () => {
await ui.app.setup()
const lines = await browser.execute((options) => {

View file

@ -19,6 +19,9 @@ export class TextManager {
constructor(public app: App) {}
getTextElement() {
const oldElm = document.querySelector('.tl-text-measure')
oldElm?.remove()
const elm = document.createElement('div')
this.app.getContainer().appendChild(elm)
@ -59,8 +62,6 @@ export class TextManager {
const rect = elm.getBoundingClientRect()
elm.remove()
return {
x: 0,
y: 0,
@ -252,7 +253,7 @@ export class TextManager {
elm.remove()
// We're done! Join the words in each line.
const result: string[] = lines.map((line) => line.join(''))
const result: string[] = lines.map((line) => line.join('').trimEnd())
return result
}

View file

@ -910,13 +910,13 @@ export class TLArrowUtil extends TLShapeUtil<TLArrowShape> {
props: { text },
} = shape
if (text.trim() !== shape.props.text) {
if (text.trimEnd() !== shape.props.text) {
this.app.updateShapes([
{
id,
type,
props: {
text: text.trim(),
text: text.trimEnd(),
},
},
])

View file

@ -324,13 +324,13 @@ export class TLGeoUtil extends TLBoxUtil<TLGeoShape> {
props: { text },
} = shape
if (text.trim() !== shape.props.text) {
if (text.trimEnd() !== shape.props.text) {
this.app.updateShapes([
{
id,
type,
props: {
text: text.trim(),
text: text.trimEnd(),
},
},
])
@ -808,8 +808,8 @@ export class TLGeoUtil extends TLBoxUtil<TLGeoShape> {
}
onBeforeUpdate = (prev: TLGeoShape, next: TLGeoShape) => {
const prevText = prev.props.text.trim()
const nextText = next.props.text.trim()
const prevText = prev.props.text.trimEnd()
const nextText = next.props.text.trimEnd()
if (
prevText === nextText &&
@ -876,7 +876,7 @@ export class TLGeoUtil extends TLBoxUtil<TLGeoShape> {
props: {
...next.props,
growY,
w: nextWidth,
w: Math.max(next.props.w, nextWidth),
},
}
}
@ -921,7 +921,7 @@ export class TLGeoUtil extends TLBoxUtil<TLGeoShape> {
}
function getLabelSize(app: App, shape: TLGeoShape) {
const text = shape.props.text.trim()
const text = shape.props.text.trimEnd()
if (!text) {
return { w: 0, h: 0 }

View file

@ -206,13 +206,13 @@ export class TLNoteUtil extends TLShapeUtil<TLNoteShape> {
props: { text },
} = shape
if (text.trim() !== shape.props.text) {
if (text.trimEnd() !== shape.props.text) {
this.app.updateShapes([
{
id,
type,
props: {
text: text.trim(),
text: text.trimEnd(),
},
},
])

View file

@ -263,7 +263,7 @@ export class TLTextUtil extends TLShapeUtil<TLTextShape> {
props: { text },
} = shape
const trimmedText = shape.props.text.trim()
const trimmedText = shape.props.text.trimEnd()
if (trimmedText.length === 0) {
this.app.deleteShapes([shape.id])
@ -274,7 +274,7 @@ export class TLTextUtil extends TLShapeUtil<TLTextShape> {
id,
type,
props: {
text: text.trim(),
text: text.trimEnd(),
},
},
])