From 9225c9d31b34ba1edb979de4cba54edc210d08bd Mon Sep 17 00:00:00 2001 From: resaldiv <72623351+resaldiv@users.noreply.github.com> Date: Tue, 20 Oct 2020 14:41:28 -0700 Subject: [PATCH 1/3] Add support to return a span element without a code wrap --- __tests__/index.spec.tsx | 15 +++++++++++++++ src/index.ts | 9 ++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/__tests__/index.spec.tsx b/__tests__/index.spec.tsx index c7aef42..16006d3 100644 --- a/__tests__/index.spec.tsx +++ b/__tests__/index.spec.tsx @@ -193,6 +193,21 @@ describe("Ansi", () => { ); }); + test("can spanify", () => { + const el = shallow( + React.createElement( + Ansi, + { spanify: true }, + "spanify works" + ) + ); + expect(el).not.toBeNull(); + expect(el.text()).toBe("spanify works"); + expect(el.html()).toBe( + 'spanify works' + ); + }); + describe("useClasses options", () => { test("can add the font color class", () => { const el = shallow( diff --git a/src/index.ts b/src/index.ts index 888670a..e92b791 100644 --- a/src/index.ts +++ b/src/index.ts @@ -143,13 +143,16 @@ declare interface Props { linkify?: boolean; className?: string; useClasses?: boolean; + spanify?: boolean; } export default function Ansi(props: Props): JSX.Element { - const { className, useClasses, children, linkify } = props; + const { className, useClasses, children, linkify, spanify } = props; + const elementType = spanify ? React.Fragment : "code"; + const elementProps = spanify ? null : { className }; return React.createElement( - "code", - { className }, + elementType, + elementProps, ansiToJSON(children ?? "", useClasses ?? false).map( convertBundleIntoReact.bind(null, linkify ?? false, useClasses ?? false) ) From fefe5ef3a4b404862de3cacc083e81dfa5c1d85b Mon Sep 17 00:00:00 2001 From: resaldiv <72623351+resaldiv@users.noreply.github.com> Date: Thu, 22 Oct 2020 13:42:13 -0700 Subject: [PATCH 2/3] Change spanify prop to be as --- __tests__/index.spec.tsx | 50 ++++++++++++++++++++++++++++------------ src/index.ts | 8 +++---- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/__tests__/index.spec.tsx b/__tests__/index.spec.tsx index 16006d3..0d5c7e3 100644 --- a/__tests__/index.spec.tsx +++ b/__tests__/index.spec.tsx @@ -193,21 +193,6 @@ describe("Ansi", () => { ); }); - test("can spanify", () => { - const el = shallow( - React.createElement( - Ansi, - { spanify: true }, - "spanify works" - ) - ); - expect(el).not.toBeNull(); - expect(el.text()).toBe("spanify works"); - expect(el.html()).toBe( - 'spanify works' - ); - }); - describe("useClasses options", () => { test("can add the font color class", () => { const el = shallow( @@ -284,4 +269,39 @@ 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( + 'hello world' + ); + }); + + 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( + 'https://nteract.io/' + ); + }); + + 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( + 'hello world' + ); + }); + }); }); diff --git a/src/index.ts b/src/index.ts index e92b791..764842f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -143,13 +143,13 @@ declare interface Props { linkify?: boolean; className?: string; useClasses?: boolean; - spanify?: boolean; + as?: string; } export default function Ansi(props: Props): JSX.Element { - const { className, useClasses, children, linkify, spanify } = props; - const elementType = spanify ? React.Fragment : "code"; - const elementProps = spanify ? null : { className }; + const { className, useClasses, children, linkify, as } = props; + const elementType = ( as === "span" ) ? React.Fragment : "code"; + const elementProps = ( as === "span" ) ? null : { className }; return React.createElement( elementType, elementProps, From a0f6252d62866fc74fed42e36d67598eb9256e51 Mon Sep 17 00:00:00 2001 From: resaldiv <72623351+resaldiv@users.noreply.github.com> Date: Wed, 28 Oct 2020 19:03:00 -0700 Subject: [PATCH 3/3] Add support to use anser as a prop --- __tests__/index.spec.tsx | 46 ++++++++++++++++++++++++++++++++++++++++ src/index.ts | 10 +++++---- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/__tests__/index.spec.tsx b/__tests__/index.spec.tsx index 0d5c7e3..9120e54 100644 --- a/__tests__/index.spec.tsx +++ b/__tests__/index.spec.tsx @@ -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", () => { @@ -304,4 +306,48 @@ describe("Ansi", () => { ); }); }); + + 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( + 'hello world' + ); + }); + }); + + 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( + 'hello world' + ); + }); + + 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( + 'hello world' + ); + }); + }); + }); }); diff --git a/src/index.ts b/src/index.ts index 764842f..7b1bfe7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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 @@ -144,16 +145,17 @@ declare interface Props { className?: string; useClasses?: boolean; as?: string; + anser?: Anser; } export default function Ansi(props: Props): JSX.Element { - const { className, useClasses, children, linkify, as } = 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( elementType, elementProps, - ansiToJSON(children ?? "", useClasses ?? false).map( + ansiToJSON(children ?? "", useClasses ?? false, anser ?? new Anser()).map( convertBundleIntoReact.bind(null, linkify ?? false, useClasses ?? false) ) );