Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to return a span element without a code wrap #46

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions __tests__/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { shallow } from "enzyme";
import React from "react";

import Anser from "anser";
import Ansi from "../src/index";

const GREEN_FG = "\u001b[32m";
const YELLOW_BG = "\u001b[43m";
const BOLD = "\u001b[1m";
const RESET = "\u001b[0;m";
const NO_OP = "\u001b[99m";

describe("Ansi", () => {
test("hello world", () => {
Expand Down Expand Up @@ -269,4 +271,83 @@ describe("Ansi", () => {
);
});
});

describe("as span", () => {
test("can return hello world as a span element", () => {
const el = shallow(
React.createElement(Ansi, { as: "span" }, "hello world")
);
expect(el).not.toBeNull();
expect(el.text()).toBe("hello world");
expect(el.html()).toBe(
'<span>hello world</span>'
);
});

test("can return a link as a span element", () => {
const el = shallow(
React.createElement(Ansi, { as: "span", linkify: true }, "https://nteract.io/")
);
expect(el).not.toBeNull();
expect(el.text()).toBe("https://nteract.io/");
expect(el.html()).toBe(
'<span><a href="https://nteract.io/" target="_blank">https://nteract.io/</a></span>'
);
});

test("can return nested span elements with color", () => {
const el = shallow(
React.createElement(Ansi, { as: "span" }, `${GREEN_FG}hello ${YELLOW_BG}world`)
);
expect(el).not.toBeNull();
expect(el.text()).toBe("hello world");
expect(el.html()).toBe(
'<span style="color:rgb(0, 187, 0)">hello </span><span style="background-color:rgb(187, 187, 0);color:rgb(0, 187, 0)">world</span>'
);
});
});

describe("data in chunks", () => {
describe("without anser prop", () => {
test("doesn't carry colors over", () => {
const dataInChunks = [`${GREEN_FG}hello `, `${YELLOW_BG}world`];
let el = "";
dataInChunks.map((spandata) =>
el += (shallow(React.createElement(Ansi, { as: "span" }, spandata)).html())
);
expect(el).not.toBeNull();
expect(el).toBe(
'<span style="color:rgb(0, 187, 0)">hello </span><span style="background-color:rgb(187, 187, 0)">world</span>'
);
});
});

describe("with anser prop", () => {
test("can carry colors over", () => {
const anser = new Anser();
const dataInChunks = [`${GREEN_FG}hello `, `${YELLOW_BG}world`];
let el = "";
dataInChunks.map((spandata) =>
el += (shallow(React.createElement(Ansi, { as: "span", anser: anser }, spandata)).html())
);
expect(el).not.toBeNull();
expect(el).toBe(
'<span style="color:rgb(0, 187, 0)">hello </span><span style="background-color:rgb(187, 187, 0);color:rgb(0, 187, 0)">world</span>'
);
});

test("can carry colors over using a no-op option for chunks without code", () => {
const anser = new Anser();
const dataInChunks = [`${GREEN_FG}hello `, `${NO_OP}world`];
let el = "";
dataInChunks.map((spandata) =>
el += (shallow(React.createElement(Ansi, { as: "span", anser: anser }, spandata)).html())
);
expect(el).not.toBeNull();
expect(el).toBe(
'<span style="color:rgb(0, 187, 0)">hello </span><span style="color:rgb(0, 187, 0)">world</span>'
);
});
});
});
});
17 changes: 11 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import * as React from "react";
*/
function ansiToJSON(
input: string,
use_classes: boolean = false
use_classes: boolean = false,
anser: Anser
): AnserJsonEntry[] {
input = escapeCarriageReturn(input);
return Anser.ansiToJson(input, {
return anser.ansiToJson(input, {
json: true,
remove_empty: true,
use_classes
Expand Down Expand Up @@ -143,14 +144,18 @@ declare interface Props {
linkify?: boolean;
className?: string;
useClasses?: boolean;
as?: string;
anser?: Anser;
}

export default function Ansi(props: Props): JSX.Element {
const { className, useClasses, children, linkify } = props;
const { className, useClasses, children, linkify, as, anser } = props;
const elementType = ( as === "span" ) ? React.Fragment : "code";
const elementProps = ( as === "span" ) ? null : { className };
return React.createElement(
"code",
{ className },
ansiToJSON(children ?? "", useClasses ?? false).map(
elementType,
elementProps,
ansiToJSON(children ?? "", useClasses ?? false, anser ?? new Anser()).map(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a big no-no in React because it means that your renders are no longer pure (the component ends up mutating the internal state of the Anser instance).

convertBundleIntoReact.bind(null, linkify ?? false, useClasses ?? false)
)
);
Expand Down