Skip to content

Commit

Permalink
Fix broken layout when emojis are used (#536)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucas-labs committed Mar 11, 2023
1 parent 6278b81 commit 96b6c46
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 2 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"cli-cursor": "^4.0.0",
"cli-truncate": "^3.1.0",
"code-excerpt": "^4.0.0",
"emoji-regex": "^10.2.1",
"indent-string": "^5.0.0",
"is-ci": "^3.0.1",
"lodash-es": "^4.17.21",
Expand Down
11 changes: 9 additions & 2 deletions src/output.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import sliceAnsi from 'slice-ansi';
import stringWidth from 'string-width';
import widestLine from 'widest-line';
import createEmojiRegex from 'emoji-regex';
import {type OutputTransformer} from './render-node-to-output.js';

const emojiRegex = createEmojiRegex();

/**
* "Virtual" output class
*
Expand Down Expand Up @@ -184,10 +187,14 @@ export default class Output {
line = transformer(line);
}

// `slice-ansi` counts emojis as one character, not two, so
// indexes must be adjusted to subtract number of emojis
const emojiCount = currentLine.match(emojiRegex)?.length ?? 0;

output[y + offsetY] =
sliceAnsi(currentLine, 0, x) +
sliceAnsi(currentLine, 0, x - emojiCount) +
line +
sliceAnsi(currentLine, x + width);
sliceAnsi(currentLine, x - emojiCount + width);

offsetY++;
}
Expand Down
121 changes: 121 additions & 0 deletions test/borders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import test from 'ava';
import boxen, {type Options} from 'boxen';
import indentString from 'indent-string';
import delay from 'delay';
import widestLine from 'widest-line';
import {render, Box, Text} from '../src/index.js';
import {renderToString} from './helpers/render-to-string.js';
import createStdout from './helpers/create-stdout.js';
Expand Down Expand Up @@ -289,6 +290,126 @@ test('nested boxes', t => {
);
});

test('nested boxes - fit-content box with wide characters on flex-direction row', t => {
const output = renderToString(
<Box borderStyle="round" alignSelf="flex-start">
<Box borderStyle="round">
<Text>ミスター</Text>
</Box>
<Box borderStyle="round">
<Text>スポック</Text>
</Box>
<Box borderStyle="round">
<Text>カーク船長</Text>
</Box>
</Box>
);

const box1 = boxen('ミスター', {borderStyle: 'round'});
const box2 = boxen('スポック', {borderStyle: 'round'});
const box3 = boxen('カーク船長', {borderStyle: 'round'});

const expected = boxen(
box1
.split('\n')
.map(
(line, index) =>
line + box2.split('\n')[index]! + box3.split('\n')[index]!
)
.join('\n'),
{borderStyle: 'round'}
);

t.is(output, expected);
});

test('nested boxes - fit-content box with emojis on flex-direction row', t => {
const output = renderToString(
<Box borderStyle="round" alignSelf="flex-start">
<Box borderStyle="round">
<Text>🦾</Text>
</Box>
<Box borderStyle="round">
<Text>🌏</Text>
</Box>
<Box borderStyle="round">
<Text>😋</Text>
</Box>
</Box>
);

const box1 = boxen('🦾', {borderStyle: 'round'});
const box2 = boxen('🌏', {borderStyle: 'round'});
const box3 = boxen('😋', {borderStyle: 'round'});

const expected = boxen(
box1
.split('\n')
.map(
(line, index) =>
line + box2.split('\n')[index]! + box3.split('\n')[index]!
)
.join('\n'),
{borderStyle: 'round'}
);

t.is(output, expected);
});

test('nested boxes - fit-content box with wide characters on flex-direction column', t => {
const output = renderToString(
<Box borderStyle="round" alignSelf="flex-start" flexDirection="column">
<Box borderStyle="round">
<Text>ミスター</Text>
</Box>
<Box borderStyle="round">
<Text>スポック</Text>
</Box>
<Box borderStyle="round">
<Text>カーク船長</Text>
</Box>
</Box>
);

const expected = boxen(
boxen('ミスター ', {borderStyle: 'round'}) +
'\n' +
boxen('スポック ', {borderStyle: 'round'}) +
'\n' +
boxen('カーク船長', {borderStyle: 'round'}),
{borderStyle: 'round'}
);

t.is(output, expected);
});

test('nested boxes - fit-content box with emojis on flex-direction column', t => {
const output = renderToString(
<Box borderStyle="round" alignSelf="flex-start" flexDirection="column">
<Box borderStyle="round">
<Text>🦾</Text>
</Box>
<Box borderStyle="round">
<Text>🌏</Text>
</Box>
<Box borderStyle="round">
<Text>😋</Text>
</Box>
</Box>
);

const expected = boxen(
boxen('🦾', {borderStyle: 'round'}) +
'\n' +
boxen('🌏', {borderStyle: 'round'}) +
'\n' +
boxen('😋', {borderStyle: 'round'}),
{borderStyle: 'round'}
);

t.is(output, expected);
});

test('render border after update', async t => {
const stdout = createStdout();

Expand Down

0 comments on commit 96b6c46

Please sign in to comment.