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:
committed by
Facebook GitHub Bot
parent
f5d974a26c
commit
b4d80c3f80
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user