From 8d4ff8d48e90b8b7de261f4be335db532029cacd Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Tue, 25 Aug 2020 08:48:07 -0700 Subject: [PATCH] Fixed several UI issues in Databases plugin Summary: This diff improves showing details in the databases plugin in a few ways: 1. if there is large content, it is no longer rendered of screen 1. the section is now scrollable 1. made layout vertical rather than horizontal to have more space vor values 1. show the type of a value 1. better detection of JSON versus non-json values (primitives would be picked up as json, but in contrast binary ascii such as used in instagram wouldn't). This fixes also string to be rendered incorrectly by accidentally parsing them as numbers, such as can be seen in https://fb.workplace.com/groups/flippersupport/permalink/948112715669387/ Changelog: [Databases] Fixed several layout issues when inspecting rows, and added better JSON support and support for larger responses. Reviewed By: cekkaewnumchai Differential Revision: D23317689 fbshipit-source-id: 47ab5164e25da003e0d1a4ae7ccb1537b637e572 --- .../databases/DatabaseDetailSidebar.tsx | 174 +++++++++--------- .../__test__/DatabaseDetailSidebar.node.tsx | 15 +- 2 files changed, 98 insertions(+), 91 deletions(-) diff --git a/desktop/plugins/databases/DatabaseDetailSidebar.tsx b/desktop/plugins/databases/DatabaseDetailSidebar.tsx index 64d105195..64f714746 100644 --- a/desktop/plugins/databases/DatabaseDetailSidebar.tsx +++ b/desktop/plugins/databases/DatabaseDetailSidebar.tsx @@ -9,23 +9,25 @@ import React, {useMemo, useState, useEffect, useReducer} from 'react'; import { - Text, Input, DetailSidebar, Panel, - ManagedTable, - TableRows, - TableBodyRow, ManagedDataInspector, Value, valueToNullableString, renderValue, - Layout, Button, styled, produce, + colors, } from 'flipper'; +type TableRow = { + col: string; + type: Value['type']; + value: React.ReactElement; +}; + type DatabaseDetailSidebarProps = { columnLabels: Array; columnValues: Array; @@ -41,29 +43,48 @@ const EditTriggerSection = styled.div({ paddingRight: '10px', }); -function sidebarRows(labels: Array, values: Array): TableRows { +const TableDetailRow = styled.div({ + borderBottom: `1px solid ${colors.blackAlpha10}`, + padding: 8, +}); + +const TableDetailRowTitle = styled.div({ + fontWeight: 'bold', + marginBottom: 8, +}); + +const TableDetailRowType = styled.span({ + color: colors.light20, + marginLeft: 8, + fontWeight: 'normal', +}); + +const TableDetailRowValue = styled.div({}); + +function sidebarRows(labels: Array, values: Array): TableRow[] { return labels.map((label, idx) => buildSidebarRow(label, values[idx])); } -function buildSidebarRow(key: string, val: Value): TableBodyRow { +function buildSidebarRow(key: string, val: Value): TableRow { let output = renderValue(val, true); - // TODO(T60896483): Narrow the scope of this try/catch block. - if (val.type === 'string') { + if ( + (val.type === 'string' || val.type === 'blob') && + (val.value[0] === '[' || val.value[0] === '{') + ) { try { - const parsed = JSON.parse(val.value); - output = ( - - ); + // eslint-disable-next-line + var parsed = JSON.parse(val.value); } catch (_error) {} + if (parsed) { + output = ( + + ); + } } return { - columns: { - col: {value: {key}}, - val: { - value: output, - }, - }, - key: key, + col: key, + type: val.type, + value: output, }; } @@ -71,35 +92,32 @@ function sidebarEditableRows( labels: Array, values: Array, rowDispatch: (action: RowAction) => void, -): TableRows { +): TableRow[] { return labels.map((label, idx) => - buildSidebarEditableRow( - label, - valueToNullableString(values[idx]), - (value: string | null) => rowDispatch({type: 'set', key: label, value}), + buildSidebarEditableRow(label, values[idx], (value: string | null) => + rowDispatch({type: 'set', key: label, value}), ), ); } function buildSidebarEditableRow( key: string, - value: string | null, + val: Value, onUpdateValue: (value: string | null) => void, -): TableBodyRow { +): TableRow { + if (val.type === 'blob' || !val.type) { + return buildSidebarRow(key, val); + } return { - columns: { - col: {value: {key}}, - val: { - value: ( - - ), - }, - }, - key: key, + col: key, + type: val.type, + value: ( + + ), }; } @@ -120,24 +138,11 @@ const EditField = React.memo( }} placeholder={value === null ? 'NULL' : undefined} data-testid={'update-query-input'} + style={{width: '100%'}} /> ); }, ); -const cols = { - col: { - value: 'Column', - resizable: true, - }, - val: { - value: 'Value', - resizable: true, - }, -}; -const colSizes = { - col: '35%', - val: 'flex', -}; type RowState = {changes: {[key: string]: string | null}; updated: boolean}; type RowAction = @@ -181,37 +186,36 @@ export default React.memo(function DatabaseDetailSidebar( floating={false} collapsable={true} padded={false}> - - {onSave && ( - - {editing ? ( - <> - - - - ) : ( - - )} - - )} - - + {onSave ? ( + + {editing ? ( + <> + + + + ) : ( + + )} + + ) : null} +
+ {rows.map((row) => ( + + + {row.col} + ({row.type}) + + {row.value} + + ))} +
); diff --git a/desktop/plugins/databases/__test__/DatabaseDetailSidebar.node.tsx b/desktop/plugins/databases/__test__/DatabaseDetailSidebar.node.tsx index a0a84d637..60a4e88c8 100644 --- a/desktop/plugins/databases/__test__/DatabaseDetailSidebar.node.tsx +++ b/desktop/plugins/databases/__test__/DatabaseDetailSidebar.node.tsx @@ -134,12 +134,15 @@ test('editing some field after trigger Edit', async () => { fireEvent.click(res.getByText('Edit')); // still find all values because it needs to show up for (const value of values) { - if (value.type === 'blob') { - continue; - } - const searchValue: string = - value.type === 'null' ? 'NULL' : value.value.toString(); - expect(res.queryAllByText(searchValue).length).toBeGreaterThan(0); + const searchValue = value.value?.toString(); + expect( + (value.type === 'null' + ? res.queryAllByPlaceholderText('NULL') + : value.type === 'blob' + ? res.queryAllByText(searchValue!) + : res.queryAllByDisplayValue(searchValue!) + ).length, + ).toBeGreaterThan(0); } // expect the last one to contain value of 'db_1_column9_value'