Add support for complex types

Summary:
Complex nested arrays and objects are displayed in a modal since there isnt enough space to it practically.

Not many attributes in practice fall into this category

Reviewed By: lblasa

Differential Revision: D50595981

fbshipit-source-id: b1eda93c448de19c8803d64eb4cf105e2b6636a8
This commit is contained in:
Luke De Feo
2023-10-26 05:24:30 -07:00
committed by Facebook GitHub Bot
parent f5d974a26c
commit b4d80c3f80
2 changed files with 68 additions and 8 deletions

View File

@@ -7,9 +7,9 @@
* @format * @format
*/ */
import {Divider, Input, Typography} from 'antd'; import {Button, Divider, Input, Modal, Typography} from 'antd';
import {Panel, theme, Layout, styled} from 'flipper-plugin'; import {DataInspector, Panel, theme, Layout, styled} from 'flipper-plugin';
import React from 'react'; import React, {useState} from 'react';
import { import {
ClientNode, ClientNode,
Color, Color,
@@ -23,6 +23,12 @@ import {css, cx} from '@emotion/css';
import {upperFirst, sortBy} from 'lodash'; import {upperFirst, sortBy} from 'lodash';
import {any} from 'lodash/fp'; import {any} from 'lodash/fp';
import {InspectableColor} from '../../ClientTypes'; import {InspectableColor} from '../../ClientTypes';
import {transformAny} from '../../utils/dataTransform';
type ModalData = {
data: unknown;
title: string;
};
export function AttributesInspector({ export function AttributesInspector({
node, node,
@@ -31,11 +37,21 @@ export function AttributesInspector({
node: ClientNode; node: ClientNode;
metadata: MetadataMap; metadata: MetadataMap;
}) { }) {
const [modalData, setModalData] = useState<ModalData | null>(null);
const showComplexTypeModal = (modaldata: ModalData) => {
setModalData(modaldata);
};
const handleCancel = () => {
setModalData(null);
};
const keys = Object.keys(node.attributes); const keys = Object.keys(node.attributes);
const sections = keys const sections = keys
.map((key, _) => { .map((key, _) => {
/** /**
* The node top-level attributes refer to the displayable panels. * The node top-level attributes refer to the displayable panels aka sections.
* The panel name is obtained by querying the metadata. * The panel name is obtained by querying the metadata.
* The inspectable contains the actual attributes belonging to each panel. * The inspectable contains the actual attributes belonging to each panel.
*/ */
@@ -52,6 +68,7 @@ export function AttributesInspector({
metadata, metadata,
sectionMetadata.name, sectionMetadata.name,
sectionAttributes, sectionAttributes,
showComplexTypeModal,
); );
}) })
.filter((section) => section != null); .filter((section) => section != null);
@@ -59,13 +76,28 @@ export function AttributesInspector({
if (sections.length === 0) { if (sections.length === 0) {
return <NoData message="No data available for this element" />; return <NoData message="No data available for this element" />;
} }
return <>{sections}</>; return (
<>
{modalData != null && (
<Modal
title={modalData.title}
open
onOk={handleCancel}
onCancel={handleCancel}
footer={null}>
<DataInspector data={modalData} />
</Modal>
)}
{sections}
</>
);
} }
function AttributeSection( function AttributeSection(
metadataMap: MetadataMap, metadataMap: MetadataMap,
name: string, name: string,
inspectable: InspectableObject, inspectable: InspectableObject,
onDisplayModal: (modaldata: ModalData) => void,
) { ) {
const attributesOrSubSubsections = Object.entries(inspectable.fields).map( const attributesOrSubSubsections = Object.entries(inspectable.fields).map(
([fieldKey, attributeValue]) => { ([fieldKey, attributeValue]) => {
@@ -108,6 +140,7 @@ function AttributeSection(
if (attributeValue.type === 'object') { if (attributeValue.type === 'object') {
return ( return (
<SubSection <SubSection
onDisplayModal={onDisplayModal}
attributeName={attributeName} attributeName={attributeName}
inspectableObject={attributeValue} inspectableObject={attributeValue}
metadataMap={metadataMap} metadataMap={metadataMap}
@@ -119,6 +152,7 @@ function AttributeSection(
return ( return (
<NamedAttribute <NamedAttribute
attributeMetadata={attributeMetadata} attributeMetadata={attributeMetadata}
onDisplayModal={onDisplayModal}
key={attributeName} key={attributeName}
metadataMap={metadataMap} metadataMap={metadataMap}
name={attributeName} name={attributeName}
@@ -145,10 +179,12 @@ function SubSection({
attributeName, attributeName,
inspectableObject, inspectableObject,
metadataMap, metadataMap,
onDisplayModal,
}: { }: {
attributeName: string; attributeName: string;
inspectableObject: InspectableObject; inspectableObject: InspectableObject;
metadataMap: MetadataMap; metadataMap: MetadataMap;
onDisplayModal: (modaldata: ModalData) => void;
}) { }) {
return ( return (
<Layout.Container gap="small" padv="small"> <Layout.Container gap="small" padv="small">
@@ -166,6 +202,7 @@ function SubSection({
return ( return (
<NamedAttribute <NamedAttribute
key={key} key={key}
onDisplayModal={onDisplayModal}
name={attributeName} name={attributeName}
value={value} value={value}
attributeMetadata={attributeMetadata} attributeMetadata={attributeMetadata}
@@ -183,12 +220,14 @@ function NamedAttribute({
value, value,
metadataMap, metadataMap,
attributeMetadata, attributeMetadata,
onDisplayModal,
}: { }: {
name: string; name: string;
value: Inspectable; value: Inspectable;
attributeMetadata: Metadata; attributeMetadata: Metadata;
metadataMap: MetadataMap; metadataMap: MetadataMap;
key: string; key: string;
onDisplayModal: (modaldata: ModalData) => void;
}) { }) {
return ( return (
<Layout.Horizontal key={key} gap="small"> <Layout.Horizontal key={key} gap="small">
@@ -205,11 +244,11 @@ function NamedAttribute({
<Layout.Container style={{flex: '1 1 auto'}}> <Layout.Container style={{flex: '1 1 auto'}}>
<AttributeValue <AttributeValue
onDisplayModal={onDisplayModal}
name={name} name={name}
attributeMetadata={attributeMetadata} attributeMetadata={attributeMetadata}
metadataMap={metadataMap} metadataMap={metadataMap}
inspectable={value} inspectable={value}
level={1}
/> />
</Layout.Container> </Layout.Container>
</Layout.Horizontal> </Layout.Horizontal>
@@ -320,13 +359,16 @@ function NumberGroup({values}: {values: NumberGroupValue[]}) {
} }
function AttributeValue({ function AttributeValue({
metadataMap,
name,
onDisplayModal,
inspectable, inspectable,
}: { }: {
onDisplayModal: (modaldata: ModalData) => void;
attributeMetadata: Metadata; attributeMetadata: Metadata;
metadataMap: MetadataMap; metadataMap: MetadataMap;
name: string; name: string;
inspectable: Inspectable; inspectable: Inspectable;
level: number;
}) { }) {
switch (inspectable.type) { switch (inspectable.type) {
case 'boolean': case 'boolean':
@@ -416,6 +458,24 @@ function AttributeValue({
case 'color': case 'color':
return <ColorInspector inspectable={inspectable as InspectableColor} />; return <ColorInspector inspectable={inspectable as InspectableColor} />;
case 'array':
case 'object':
return (
<Button
onClick={() => {
onDisplayModal({
title: name,
data: transformAny(metadataMap, inspectable),
});
}}
style={{height: 28}}
type="ghost">
<span
style={{fontFamily: 'monospace', color: theme.textColorSecondary}}>
{inspectable.type === 'array' ? '[...]' : '{...}'}
</span>
</Button>
);
} }
return null; return null;
} }

View File

@@ -16,7 +16,7 @@ import {
MetadataId, MetadataId,
} from '../ClientTypes'; } from '../ClientTypes';
function transformAny( export function transformAny(
metadata: Map<MetadataId, Metadata>, metadata: Map<MetadataId, Metadata>,
inspectable: Inspectable, inspectable: Inspectable,
): any { ): any {