From 79c7ee43d72cc8435005878c616090f0d67976bd Mon Sep 17 00:00:00 2001 From: Tyler Krupicka Date: Mon, 22 Feb 2021 20:20:13 -0800 Subject: [PATCH 1/5] Add name, fix colon styling --- components/ObjectInspector/src/Context.tsx | 11 +++++ .../src/ObjectInspector.test.tsx | 22 ++++++++- .../ObjectInspector/src/ObjectInspector.tsx | 16 +++++-- .../ObjectInspector/src/ObjectLabel.tsx | 18 ++++++-- .../ObjectInspector/src/ObjectValue.tsx | 46 +++++++++++++++++-- .../src/stories/Features.stories.tsx | 8 ++++ .../src/stories/ObjectInspector.stories.tsx | 13 +++++- 7 files changed, 119 insertions(+), 15 deletions(-) create mode 100644 components/ObjectInspector/src/Context.tsx diff --git a/components/ObjectInspector/src/Context.tsx b/components/ObjectInspector/src/Context.tsx new file mode 100644 index 0000000..2350b7d --- /dev/null +++ b/components/ObjectInspector/src/Context.tsx @@ -0,0 +1,11 @@ +import React from "react"; + +export interface ObjectInspectorContextProps { + /** The key for the root object */ + name?: string; +} + +/** Context that gives sub-components access to top-level props */ +export const ObjectInspectorContext = React.createContext< + ObjectInspectorContextProps +>({ name: "" }); diff --git a/components/ObjectInspector/src/ObjectInspector.test.tsx b/components/ObjectInspector/src/ObjectInspector.test.tsx index 392b5f7..aa4cd11 100644 --- a/components/ObjectInspector/src/ObjectInspector.test.tsx +++ b/components/ObjectInspector/src/ObjectInspector.test.tsx @@ -56,6 +56,26 @@ describe("ObjectInspector", () => { }); }); + test("Displays names", async () => { + const nested = { + one: { + two: { + three: "test", + }, + }, + }; + await act(async () => { + const { getByText } = render(); + waitFor(() => expect(getByText("nameTest:")).not.toBeDefined()); + }); + act(() => { + const { getByText } = render( + + ); + waitFor(() => expect(getByText("nameTest:")).toBeDefined()); + }); + }); + test("It calls onSelect correctly", async () => { const onSelect = jest.fn(); const nested = { @@ -77,7 +97,7 @@ describe("ObjectInspector", () => { await waitFor(() => expect(getByText('"test"')).toBeDefined()); - const two = getByText("two:"); + const two = getByText("two"); fireEvent.click(two); diff --git a/components/ObjectInspector/src/ObjectInspector.tsx b/components/ObjectInspector/src/ObjectInspector.tsx index 0fb8237..63b1539 100644 --- a/components/ObjectInspector/src/ObjectInspector.tsx +++ b/components/ObjectInspector/src/ObjectInspector.tsx @@ -7,6 +7,7 @@ import { ResolvedASTNode, } from "@devtools-ds/object-parser"; import { ThemeableElement, useTheme, ThemeProvider } from "@devtools-ds/themes"; +import { ObjectInspectorContext } from "./Context"; import ObjectInspectorItem from "./ObjectInspectorItem"; import styles from "./ObjectInspector.css"; @@ -15,6 +16,8 @@ interface ObjectInspectorProps extends Omit, "onSelect"> { /** JSON data to render in the tree. */ data: SupportedTypes; + /** The key for the root object */ + name?: string; /** Depth of the tree that is open at first render. */ expandLevel: number; /** Whether to sort keys like the browsers do. */ @@ -36,6 +39,7 @@ export const ObjectInspector = (props: ObjectInspectorProps) => { theme, colorScheme, onSelect, + name, ...html } = props; const [ast, setAST] = useState(undefined); @@ -61,11 +65,13 @@ export const ObjectInspector = (props: ObjectInspectorProps) => { > {ast && ( - + + + )} diff --git a/components/ObjectInspector/src/ObjectLabel.tsx b/components/ObjectInspector/src/ObjectLabel.tsx index 7cb9ba6..385aff5 100644 --- a/components/ObjectInspector/src/ObjectLabel.tsx +++ b/components/ObjectInspector/src/ObjectLabel.tsx @@ -11,6 +11,7 @@ import { ASTNode, } from "@devtools-ds/object-parser"; import ObjectValue from "./ObjectValue"; +import { ObjectInspectorContext } from "./Context"; import styles from "./ObjectInspector.css"; interface ObjectLabelProps extends ThemeableElement<"span"> { @@ -176,6 +177,7 @@ export const ObjectLabel = (props: ObjectLabelProps) => { className, ...html } = props; + const { name } = React.useContext(ObjectInspectorContext); const { themeClass, currentTheme } = useTheme({ theme, colorScheme }, styles); const isPrototype = ast.isPrototype || false; const classes = makeClass(styles.objectLabel, themeClass, className, { @@ -186,10 +188,20 @@ export const ObjectLabel = (props: ObjectLabelProps) => { /** The key for the node */ const Key = () => { + let { key } = ast; + if (isRoot && name) key = name; + + if (isRoot && !name) { + return null; + } + return ( - - {isRoot ? "" : `${ast.key}: `} - + <> + + {key} + + {key && } + ); }; diff --git a/components/ObjectInspector/src/ObjectValue.tsx b/components/ObjectInspector/src/ObjectValue.tsx index 91deef9..670eced 100644 --- a/components/ObjectInspector/src/ObjectValue.tsx +++ b/components/ObjectInspector/src/ObjectValue.tsx @@ -3,6 +3,7 @@ import makeClass from "clsx"; import { ThemeableElement, useTheme } from "@devtools-ds/themes"; import { ASTNode, isObject, getPromiseState } from "@devtools-ds/object-parser"; import styles from "./ObjectInspector.css"; +import { ObjectInspectorContext } from "./Context"; interface ObjectValueProps extends ThemeableElement<"span"> { /** Type of object. */ @@ -18,6 +19,7 @@ interface ObjectValueProps extends ThemeableElement<"span"> { * @param value - The value string * @param valueClass - The class to apply to the value * @param showKey - Whether or not to show the key with the value + * @param name - The name if the value is the root * @param depth - Current depth (so we don't put a key on root) */ const buildValue = ( @@ -25,13 +27,15 @@ const buildValue = ( value: React.ReactNode, valueClass: string, showKey: boolean, + name: string, depth: number ) => { - const computedKey = key.includes("-") ? `"${key}"` : key; + let computedKey = key.includes("-") ? `"${key}"` : key; const isRoot = depth <= 0; + if (isRoot && name) computedKey = name; return ( - {!isRoot && showKey && ( + {(!isRoot || name) && showKey && ( <> {computedKey} @@ -45,6 +49,8 @@ const buildValue = ( /** Display a leaf key-value pair with appropriate styles. */ export const ObjectValue = (props: ObjectValueProps) => { + const context = React.useContext(ObjectInspectorContext); + const name = context?.name || ""; const { ast, theme, showKey, colorScheme, className, ...html } = props; const { themeClass } = useTheme({ theme, colorScheme }, styles); const [asyncValue, setAsyncValue] = useState(); @@ -61,6 +67,7 @@ export const ObjectValue = (props: ObjectValueProps) => { `Promise { "${await getPromiseState(promise)}" }`, styles.key, showKey, + name, ast.depth ) ); @@ -77,6 +84,7 @@ export const ObjectValue = (props: ObjectValueProps) => { String(ast.value), styles.number, showKey, + name, ast.depth ); } else if (typeof ast.value === "boolean") { @@ -86,6 +94,7 @@ export const ObjectValue = (props: ObjectValueProps) => { String(ast.value), styles.boolean, showKey, + name, ast.depth ); } else if (typeof ast.value === "string") { @@ -95,6 +104,7 @@ export const ObjectValue = (props: ObjectValueProps) => { `"${ast.value}"`, styles.string, showKey, + name, ast.depth ); } else if (typeof ast.value === "undefined") { @@ -104,6 +114,7 @@ export const ObjectValue = (props: ObjectValueProps) => { "undefined", styles.undefined, showKey, + name, ast.depth ); } else if (typeof ast.value === "symbol") { @@ -113,6 +124,7 @@ export const ObjectValue = (props: ObjectValueProps) => { ast.value.toString(), styles.string, showKey, + name, ast.depth ); } else if (typeof ast.value === "function") { @@ -122,12 +134,20 @@ export const ObjectValue = (props: ObjectValueProps) => { `${ast.value.name}()`, styles.key, showKey, + name, ast.depth ); } else if (typeof ast.value === "object") { if (ast.value === null) { // Null - value = buildValue(ast.key, "null", styles.null, showKey, ast.depth); + value = buildValue( + ast.key, + "null", + styles.null, + showKey, + name, + ast.depth + ); } else if (Array.isArray(ast.value)) { // Array value = buildValue( @@ -135,6 +155,7 @@ export const ObjectValue = (props: ObjectValueProps) => { `Array(${ast.value.length})`, styles.key, showKey, + name, ast.depth ); } else if (ast.value instanceof Date) { @@ -144,6 +165,7 @@ export const ObjectValue = (props: ObjectValueProps) => { `Date ${ast.value.toString()}`, styles.value, showKey, + name, ast.depth ); } else if (ast.value instanceof RegExp) { @@ -153,6 +175,7 @@ export const ObjectValue = (props: ObjectValueProps) => { ast.value.toString(), styles.regex, showKey, + name, ast.depth ); } else if (ast.value instanceof Error) { @@ -162,11 +185,25 @@ export const ObjectValue = (props: ObjectValueProps) => { ast.value.toString(), styles.error, showKey, + name, + ast.depth + ); +<<<<<<< HEAD +======= + } else if (typeof Buffer !== "undefined" && Buffer.isBuffer(ast.value)) { + // Buffer + value = buildValue( + ast.key, + `Buffer[${ast.value.length}]`, + styles.value, + showKey, + name, ast.depth ); +>>>>>>> Add name, fix colon styling } else if (isObject(ast.value)) { // Object - value = buildValue(ast.key, "{…}", styles.key, showKey, ast.depth); + value = buildValue(ast.key, "{…}", styles.key, showKey, name, ast.depth); } else { // WeakMap, WeakSet, Custom Classes, etc value = buildValue( @@ -174,6 +211,7 @@ export const ObjectValue = (props: ObjectValueProps) => { ast.value.constructor.name, styles.key, showKey, + name, ast.depth ); } diff --git a/components/ObjectInspector/src/stories/Features.stories.tsx b/components/ObjectInspector/src/stories/Features.stories.tsx index 55a2a0f..20c1502 100644 --- a/components/ObjectInspector/src/stories/Features.stories.tsx +++ b/components/ObjectInspector/src/stories/Features.stories.tsx @@ -72,6 +72,14 @@ export const DisablePrototypes = () => ( ); +export const NamedObject = () => ( + +); + +export const NamedValue = () => ( + +); + export const FontInheritance = () => (
diff --git a/components/ObjectInspector/src/stories/ObjectInspector.stories.tsx b/components/ObjectInspector/src/stories/ObjectInspector.stories.tsx index a773afa..0cbf59c 100644 --- a/components/ObjectInspector/src/stories/ObjectInspector.stories.tsx +++ b/components/ObjectInspector/src/stories/ObjectInspector.stories.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { boolean, number } from "@storybook/addon-knobs"; +import { boolean, number, text } from "@storybook/addon-knobs"; import { action } from "@storybook/addon-actions"; import { ObjectInspector } from "../ObjectInspector"; import notes from "../../README.md"; @@ -59,11 +59,20 @@ const data = { }, }; +const nested = { + one: { + two: { + three: "test", + }, + }, +}; + export const Playground = () => { const onSelect = action("onSelect"); return ( Date: Mon, 22 Feb 2021 20:23:53 -0800 Subject: [PATCH 2/5] add name to overview --- .../ObjectInspector/src/stories/Overview.stories.mdx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/ObjectInspector/src/stories/Overview.stories.mdx b/components/ObjectInspector/src/stories/Overview.stories.mdx index a7792e0..5f84ddf 100644 --- a/components/ObjectInspector/src/stories/Overview.stories.mdx +++ b/components/ObjectInspector/src/stories/Overview.stories.mdx @@ -60,6 +60,14 @@ Similarly, you can disable object `prototypes` from being included. +### Named Root + +You can add a `name` to add a descriptive key to the root node, + + + + + ### onSelect You can use the `onSelect` prop to get the `@devtools-ds/object-parser` AST node for the currently selected part of the object. From d0a6706b1a0ee0901578e46d1d2cd17d0ae955f0 Mon Sep 17 00:00:00 2001 From: Tyler Krupicka Date: Sun, 14 Mar 2021 16:10:22 -0700 Subject: [PATCH 3/5] fix object value after rebase --- components/ObjectInspector/src/ObjectValue.tsx | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/components/ObjectInspector/src/ObjectValue.tsx b/components/ObjectInspector/src/ObjectValue.tsx index 670eced..59e1c1c 100644 --- a/components/ObjectInspector/src/ObjectValue.tsx +++ b/components/ObjectInspector/src/ObjectValue.tsx @@ -188,19 +188,6 @@ export const ObjectValue = (props: ObjectValueProps) => { name, ast.depth ); -<<<<<<< HEAD -======= - } else if (typeof Buffer !== "undefined" && Buffer.isBuffer(ast.value)) { - // Buffer - value = buildValue( - ast.key, - `Buffer[${ast.value.length}]`, - styles.value, - showKey, - name, - ast.depth - ); ->>>>>>> Add name, fix colon styling } else if (isObject(ast.value)) { // Object value = buildValue(ast.key, "{…}", styles.key, showKey, name, ast.depth); From d81e139a537e249c870b878e8095615b05c2a94c Mon Sep 17 00:00:00 2001 From: Tyler Krupicka Date: Sun, 14 Mar 2021 16:18:22 -0700 Subject: [PATCH 4/5] export browser theme function --- packages/themes/src/AutoThemeProvider.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/themes/src/AutoThemeProvider.tsx b/packages/themes/src/AutoThemeProvider.tsx index 2d1e27d..484b593 100644 --- a/packages/themes/src/AutoThemeProvider.tsx +++ b/packages/themes/src/AutoThemeProvider.tsx @@ -3,14 +3,14 @@ import { all } from "./themes"; import { ThemeableElement, ThemeContext } from "./utils"; /** Determine if the current browser is FireFox */ -const isFirefox = () => { +export const getBrowserTheme = () => { if (window?.navigator?.userAgent) { if (window.navigator.userAgent.toLowerCase().includes("firefox")) { - return true; + return "firefox"; } } - return false; + return "chrome"; }; export interface AutoThemeProviderProps extends ThemeableElement<"div"> { @@ -63,7 +63,7 @@ export const AutoThemeProvider = ({ }: AutoThemeProviderProps) => { const isDark = useDarkMode(); const colorScheme = propsColorScheme || (isDark ? "dark" : "light"); - const theme = propsTheme || (isFirefox() ? "firefox" : "chrome"); + const theme = propsTheme || getBrowserTheme(); const style = { backgroundColor: all[theme][colorScheme].backgroundColor, color: all[theme][colorScheme].textColor, From 5fafeabb33d280114640ce4171971ba3829164c0 Mon Sep 17 00:00:00 2001 From: Tyler Krupicka Date: Sun, 14 Mar 2021 16:27:36 -0700 Subject: [PATCH 5/5] try releasing canaries --- .circleci/config.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0d51095..2fb5857 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -165,7 +165,3 @@ workflows: requires: - lint - unit-tests - filters: - branches: - only: - - master