Skip to content

Commit

Permalink
Better table layout and bug fixes (#126)
Browse files Browse the repository at this point in the history
  • Loading branch information
gagik authored Feb 28, 2023
1 parent 0073b8e commit b617797
Show file tree
Hide file tree
Showing 16 changed files with 1,150 additions and 862 deletions.
1,136 changes: 679 additions & 457 deletions flipper-plugin-realm/package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions flipper-plugin-realm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"@types/react-infinite-scroller": "^1.2.3",
"@typescript-eslint/eslint-plugin": "^5.32.0",
"@typescript-eslint/parser": "^5.32.0",
"antd": "latest",
"antd": "^5.2.2",
"css-loader": "^6.7.1",
"dependency-cruiser": "^11.14.2",
"eslint": "^8.21.0",
Expand Down Expand Up @@ -93,7 +93,7 @@
]
},
"dependencies": {
"@ant-design/icons": "^4.7.0",
"@ant-design/icons": "^5.0.1",
"bson": "^4.6.5",
"dot": "^1.1.3",
"electron": "^20.0.3",
Expand Down
44 changes: 44 additions & 0 deletions flipper-plugin-realm/src/components/ClickableText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from "react";
import { useState } from "react";

type ClickableTextProps = {
/** Content to be displayed for the given value. */
displayValue: string | number | JSX.Element;
ellipsis?: boolean;
onClick: VoidFunction;
};

/** Functional component to render clickable text which opens the DataInspector.*/
export const ClickableText = ({
displayValue,
ellipsis = false,
onClick,
}: ClickableTextProps) => {
const [isHovering, setHovering] = useState(false);
return (
<div>
<div
style={{
cursor: 'pointer',
display: 'inline',
color: ellipsis ? undefined : '#6831c7',
textDecoration: isHovering ? 'underline' : undefined,
}}
onClick={onClick}
onMouseEnter={() => setHovering(true)}
onMouseLeave={() => setHovering(false)}
>
{displayValue}
{ellipsis ? (
<div
style={{
display: 'inline',
}}
>
...
</div>
) : null}
</div>
</div>
);
};
113 changes: 25 additions & 88 deletions flipper-plugin-realm/src/components/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { InspectionDataType } from './RealmDataInspector';
import { renderValue } from '../utils/Renderer';
import { ColumnTitle } from './ColumnTitle';
import { DropdownPropertyType, MenuItemGenerator, PlainRealmObject, DeserializedRealmObject, SortedObjectSchema, RealmObjectReference } from '../CommonTypes';
import { ClickableText } from './ClickableText';

export type ColumnType = {
optional: boolean;
Expand Down Expand Up @@ -47,15 +48,6 @@ type DataTableProps = {
getObject: (object: RealmObjectReference, objectSchemaName: string) => Promise<DeserializedRealmObject | null>;
};

type ClickableTextProps = {
/** Content to be displayed for the given value. */
displayValue: string | number | JSX.Element;
isLongString: boolean;
value: PlainRealmObject | RealmObjectReference;
isReference?: boolean;
inspectorView: 'object' | 'property';
};

// Receives a schema and returns column objects for the table.
const schemaObjectToColumns = (
schema: SortedObjectSchema,
Expand Down Expand Up @@ -132,44 +124,6 @@ export const DataTable = (dataTableProps: DataTableProps) => {
return <Layout.Container style={{padding: 20}}>No schemas found. Check selected Realm.</Layout.Container>;
}

/** Functional component to render clickable text which opens the DataInspector.*/
const ClickableText = ({
displayValue,
isLongString,
value,
inspectorView,
isReference = false,
}: ClickableTextProps) => {
const [isHovering, setHovering] = useState(false);
return (
<div>
<div
style={{
display: 'inline',
color: isLongString ? undefined : '#6831c7',
textDecoration: isHovering ? 'underline' : undefined,
}}
onClick={() => {
setNewInspectionData({ data: value, view: inspectorView, isReference }, true);
}}
onMouseEnter={() => setHovering(true)}
onMouseLeave={() => setHovering(false)}
>
{displayValue}
</div>
{isLongString ? (
<div
style={{
display: 'inline',
}}
>
...
</div>
) : null}
</div>
);
};

/** Definition of antd-specific columns. This constant is passed to the antd table as a property. */
const antdColumns:ColumnsType<DeserializedRealmObject> = schemaObjectToColumns(currentSchema).map((column) => {
const property: Realm.CanonicalObjectSchemaProperty =
Expand All @@ -181,7 +135,16 @@ export const DataTable = (dataTableProps: DataTableProps) => {
on top of the pure value specified in the 'dataSource' property of the antd table.*/
const render = (value: PlainRealmObject | RealmObjectReference, row: DeserializedRealmObject) => {
/** Apply the renderValue function on the value in the cell to create a standard cell. */
const cellValue = renderValue(value, property, schemas, instance.downloadData);
const cellValue = renderValue(value, property, schemas, {
downloadData: instance.downloadData,
inspectValue: (value: any) => {
setNewInspectionData({
data: { [`${currentSchema.name}.${column.name}`]: value},
view: "property",
isReference: false,
}, true)
}
});

/** Render buttons to expand the row and a clickable text if the cell contains a linked or embedded Realm object. */
if (value !== null && linkedSchema && property.type === 'object') {
Expand Down Expand Up @@ -216,45 +179,27 @@ export const DataTable = (dataTableProps: DataTableProps) => {
ghost
/>}
{
<ClickableText
value={
isEmbedded
? { [`${currentSchema.name}.${column.name}`]: value}
: value
}
displayValue={cellValue}
isLongString={false}
inspectorView={isEmbedded ? "property" : "object"}
isReference={!isEmbedded}
/>
<ClickableText
displayValue={cellValue}
onClick={() => {
setNewInspectionData({
data: isEmbedded ? { [`${currentSchema.name}.${column.name}`]: value} : value,
view: isEmbedded ? "property" : "object",
isReference: !isEmbedded
}, true);
}}/>
}
</Layout.Container>
);
}

/** If the cell contains a string which is too long cut it off and render it as a clickable text. */
if (typeof cellValue === 'string' && cellValue.length > 70) {
return (
<ClickableText
value={value}
displayValue={cellValue.substring(0, 70)}
isLongString={true}
inspectorView="property"
/>
);
}
return cellValue;
};
}

return {
/** Simple antd table props defined in their documentation */
minWidth: 20000,
key: property.name,
dataIndex: ["realmObject", property.name],
width: 300,
ellipsis: {
showTitle: false,
},

/** The title appearing in the tables title row. */
title: createTitle(column),
Expand Down Expand Up @@ -352,14 +297,6 @@ export const DataTable = (dataTableProps: DataTableProps) => {
}
};
return (
<div
style={{
overflow: 'auto',
height: '90%',
width: '100%',
textAlign: 'center',
}}
>
<InfiniteScroll
initialLoad={false}
pageStart={0}
Expand All @@ -370,9 +307,8 @@ export const DataTable = (dataTableProps: DataTableProps) => {
<div
style={{
marginTop: '20px',
marginBottom: '25px',
marginBottom: '100px',
display: 'inline-block',
paddingBottom: '100px',
}}
key={0}
>
Expand All @@ -381,7 +317,9 @@ export const DataTable = (dataTableProps: DataTableProps) => {
}
>
<Table
sticky={true}
style={{
wordBreak: "break-all",
}}
bordered={true}
showSorterTooltip={false}
dataSource={objects}
Expand All @@ -405,7 +343,6 @@ export const DataTable = (dataTableProps: DataTableProps) => {
scroll={{ scrollToFirstRowOnChange: false }}
/>
</InfiniteScroll>
</div>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const DataVisualizerWrapper = ({
return (
<>
<SchemaSelect schemas={schemas} />
<Layout.Container style={{height: "90%"}}>
<Layout.Container style={{height: "100%"}}>
<DataTabHeader totalObjects={totalObjects} currentSchema={currentSchema} />
<DataVisualizer
objects={objects}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { DeleteOutlined } from '@ant-design/icons';
import { Button, Col, Row } from 'antd';
import React, { useState } from 'react';
import { getDefault, TypeInput, TypeInputProps } from './TypeInput';
import { CollectionInputProps, getDefault, TypeInput } from './TypeInput';

export const ListInput = ({ property, set, defaultValue, isPrimary }: TypeInputProps) => {
const [array, setArray] = useState(defaultValue as unknown[]);
export const ListInput = ({ property, set, defaultValue, isPrimary }: CollectionInputProps) => {
// TODO: Refactor this input file to ensure proper type safety of defaultValue and cleaner structure. Same in Set.
const [array, setArray] = useState(defaultValue as unknown[] ?? []);
const [removalOffset, setRemovalOffset] = useState(0);
const typePointed = property.objectType;
if (!typePointed) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const MixedInput = ({
<Row align="middle">
<Col flex="auto">
<Tag color="success">{chosenType}</Tag>
{renderValue(value, { type, objectType: objectType?.name }, schemas, downloadData)}
{renderValue(value, { type, objectType: objectType?.name }, schemas, {downloadData})}
</Col>
<Col>
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const ObjectIdInput = ({
isPrimary
}: TypeInputProps) => {
const [_, setReset] = useState(0);
const [value, setValue] = useState<string | null>(defaultValue !== null ? (defaultValue as ObjectId).toString() : null);
const [value, setValue] = useState<string | null>(defaultValue ? (defaultValue as ObjectId).toString() : null);

const onChange = (value: string) => {
setValue(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@ import { DeleteOutlined } from "@ant-design/icons";
import { Button, Col, Row } from 'antd';
import { Layout } from "flipper-plugin";
import React, { useEffect, useState } from "react";
import { getDefault, TypeInput, TypeInputProps } from './TypeInput';
import { getDefault, TypeInput, CollectionInputProps } from './TypeInput';

export const SetInput = ({ property, set, defaultValue, isPrimary }: TypeInputProps) => {
export const SetInput = ({ property, set, defaultValue, isPrimary }: CollectionInputProps) => {
// TODO: Refactor this input file to ensure proper type safety of defaultValue and cleaner structure. Same in List.
const [_, setReset] = useState(0);
const [arr, setArr] = useState(defaultValue as unknown[]);
const [arr, setArr] = useState(defaultValue as unknown[] | undefined ?? []);
const [occurences] = useState(new Map<unknown, number>());
const [deleteOffset, setDeleteOffset] = useState(0);

const [container] = useState(new Set());
useEffect(() => {
occurences.clear();
(defaultValue as unknown[]).forEach(val => {
(defaultValue ?? []).forEach(val => {
occurences.set(val, 1);
container.add(val);
});
setArr(defaultValue as unknown[]);
setArr(defaultValue ?? []);
}, []);
const typePointed = property.objectType;
if (!typePointed) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ export type TypeInputProps = {
isPrimary: boolean;
};

export type CollectionInputProps = {
property: TypeDescription;
defaultValue?: unknown[];
set: (val: unknown[]) => void;
extraProps?: Record<string, unknown>;
isPrimary: boolean;
};

type TypeDescription = {
type: string;
optional: boolean;
Expand Down Expand Up @@ -82,9 +90,9 @@ export const TypeInput = (props: TypeInputProps) => {
case 'uuid':
return <UUIDInput {...props} />;
case 'set':
return <SetInput {...props} />;
return <SetInput {...props as CollectionInputProps} />;
case 'list':
return <ListInput {...props} />;
return <ListInput {...props as CollectionInputProps} />;
case 'mixed':
return <MixedInput {...props} />;
case 'decimal128':
Expand Down
14 changes: 5 additions & 9 deletions flipper-plugin-realm/src/pages/DataVisualizer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,18 +188,15 @@ const DataVisualizer = ({
<div
onScroll={handleScroll}
style={{
flex: '1 1 0',
width: '100%',
height: '100%',
boxSizing: 'border-box',
position: 'relative',
overflow: 'auto',
overflow: 'scroll',
// TODO: This is a temporary solution to help force the horizontal scrollbar to appear. https://github.com/realm/realm-flipper-plugin/issues/90
marginBottom: 200,
}}
>
<div
style={{
position: 'absolute',
height: '100%',
}}
>
{editingObject.object && editingObject.editing && editingObject.type === 'object' ? (
<ObjectEdit
schema={currentSchema}
Expand Down Expand Up @@ -261,7 +258,6 @@ const DataVisualizer = ({
setNewInspectionData={setNewInspectionData}
getObject={(object: RealmObjectReference) => {return getObject(selectedRealm, object.objectType!, object.objectKey)}}
/>
</div>
</div>
);

Expand Down
Loading

0 comments on commit b617797

Please sign in to comment.