From 9030a98c6e292e7e3ed035c1d4563029094c2cd0 Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Wed, 7 Apr 2021 07:52:47 -0700 Subject: [PATCH] Move DataDescription utility to flipper-plugin Summary: Another utility component used by DataInspector. Colors have been hardcoded for now, to decouple from the classic Flipper color palette. Will organize this better later in this stack, and when addressing / adding support for dark mode. Buttons, checkboxes, selects have been replaced by Antd's counterpart. Unlike ManagedDataTable, the DataInspector is primarily moved rather than copied & adapted, as the underlying abstraction / API won't change significantly. So the changes here will immediately affect all plugins in Flipper using this component. Reviewed By: passy Differential Revision: D27603126 fbshipit-source-id: bacd48c9af2b591033e7f2352627f11acb4df589 --- desktop/app/package.json | 1 - desktop/app/src/index.tsx | 4 +- desktop/app/src/ui/components/Select.tsx | 1 + .../data-inspector/DataInspector.tsx | 4 +- .../components/data-inspector/DataPreview.tsx | 4 +- desktop/app/src/ui/index.tsx | 1 - desktop/flipper-plugin/package.json | 1 + .../flipper-plugin/src/__tests__/api.node.tsx | 1 + desktop/flipper-plugin/src/index.ts | 5 + .../src/ui/__tests__/Tracked.node.tsx | 2 +- .../ui}/data-inspector/DataDescription.tsx | 243 +++++++----------- .../flipper-plugin/src/utils/parseColor.tsx | 67 +++++ 12 files changed, 181 insertions(+), 153 deletions(-) rename desktop/{app/src/ui/components => flipper-plugin/src/ui}/data-inspector/DataDescription.tsx (79%) create mode 100644 desktop/flipper-plugin/src/utils/parseColor.tsx diff --git a/desktop/app/package.json b/desktop/app/package.json index 35e28a845..7d807da66 100644 --- a/desktop/app/package.json +++ b/desktop/app/package.json @@ -53,7 +53,6 @@ "query-string": "^7.0.0", "react": "17.0.2", "react-async": "^10.0.0", - "react-color": "^2.19.3", "react-debounce-render": "^7.0.0", "react-dom": "^17.0.1", "react-element-to-jsx-string": "^14.3.1", diff --git a/desktop/app/src/index.tsx b/desktop/app/src/index.tsx index 6f60ccdc5..c318ba8e5 100644 --- a/desktop/app/src/index.tsx +++ b/desktop/app/src/index.tsx @@ -100,9 +100,9 @@ export {default as DataInspector} from './ui/components/data-inspector/DataInspe export {default as ManagedDataInspector} from './ui/components/data-inspector/ManagedDataInspector'; export {default as SearchableDataInspector} from './ui/components/data-inspector/SearchableDataInspector'; export { - default as DataDescription, + _DataDescription as DataDescription, DataDescriptionType, -} from './ui/components/data-inspector/DataDescription'; +} from 'flipper-plugin'; export {_HighlightManager as HighlightManager} from 'flipper-plugin'; export {default as Tabs} from './ui/components/Tabs'; export {default as Tab} from './ui/components/Tab'; diff --git a/desktop/app/src/ui/components/Select.tsx b/desktop/app/src/ui/components/Select.tsx index 6c4f7893a..94c56b931 100644 --- a/desktop/app/src/ui/components/Select.tsx +++ b/desktop/app/src/ui/components/Select.tsx @@ -31,6 +31,7 @@ SelectMenu.displayName = 'Select:SelectMenu'; /** * Dropdown to select from a list of options + * @deprecated use Select from antd instead: https://ant.design/components/select/ */ export default class Select extends Component<{ /** Additional className added to the element */ diff --git a/desktop/app/src/ui/components/data-inspector/DataInspector.tsx b/desktop/app/src/ui/components/data-inspector/DataInspector.tsx index 7e6154e1f..eb6972811 100644 --- a/desktop/app/src/ui/components/data-inspector/DataInspector.tsx +++ b/desktop/app/src/ui/components/data-inspector/DataInspector.tsx @@ -7,7 +7,7 @@ * @format */ -import DataDescription from './DataDescription'; +import {_DataDescription} from 'flipper-plugin'; import {MenuTemplate} from '../ContextMenu'; import {memo, useMemo, useRef, useState, useEffect, useCallback} from 'react'; import ContextMenu from '../ContextMenu'; @@ -562,7 +562,7 @@ const DataInspector: React.FC = memo( let descriptionOrPreview; if (renderExpanded || !isExpandable) { descriptionOrPreview = ( - { expect(exposedTypes.sort()).toMatchInlineSnapshot(` Array [ "Atom", + "DataDescriptionType", "DataTableColumn", "DataTableManager", "DefaultKeyboardAction", diff --git a/desktop/flipper-plugin/src/index.ts b/desktop/flipper-plugin/src/index.ts index f01a7a6e8..2b031a1f2 100644 --- a/desktop/flipper-plugin/src/index.ts +++ b/desktop/flipper-plugin/src/index.ts @@ -97,6 +97,11 @@ export { InteractiveProps as _InteractiveProps, } from './ui/Interactive'; +export { + DataDescription as _DataDescription, + DataDescriptionType, +} from './ui/data-inspector/DataDescription'; + export {useMemoize} from './utils/useMemoize'; // It's not ideal that this exists in flipper-plugin sources directly, diff --git a/desktop/flipper-plugin/src/ui/__tests__/Tracked.node.tsx b/desktop/flipper-plugin/src/ui/__tests__/Tracked.node.tsx index 10e2153d7..ac85a7cd6 100644 --- a/desktop/flipper-plugin/src/ui/__tests__/Tracked.node.tsx +++ b/desktop/flipper-plugin/src/ui/__tests__/Tracked.node.tsx @@ -9,7 +9,7 @@ import {render, fireEvent} from '@testing-library/react'; import {TestUtils} from 'flipper-plugin'; -import {sleep} from 'flipper-plugin/src/utils/sleep'; +import {sleep} from '../../utils/sleep'; import React, {Component} from 'react'; import { setGlobalInteractionReporter, diff --git a/desktop/app/src/ui/components/data-inspector/DataDescription.tsx b/desktop/flipper-plugin/src/ui/data-inspector/DataDescription.tsx similarity index 79% rename from desktop/app/src/ui/components/data-inspector/DataDescription.tsx rename to desktop/flipper-plugin/src/ui/data-inspector/DataDescription.tsx index 2a4b4a865..75dd2dffa 100644 --- a/desktop/app/src/ui/components/data-inspector/DataDescription.tsx +++ b/desktop/flipper-plugin/src/ui/data-inspector/DataDescription.tsx @@ -7,19 +7,41 @@ * @format */ -import Link from '../Link'; -import {DataInspectorSetValue} from './DataInspector'; +import {Typography, Popover, Input, Select, Checkbox} from 'antd'; +// TODO: restore import {DataInspectorSetValue} from './DataInspector'; import {PureComponent} from 'react'; import styled from '@emotion/styled'; import {SketchPicker, CompactPicker} from 'react-color'; -import Popover from '../Popover'; -import {colors} from '../colors'; -import Input from '../Input'; import React, {KeyboardEvent} from 'react'; -import Glyph from '../Glyph'; -import {_HighlightContext} from 'flipper-plugin'; -import Select from '../Select'; -import TimelineDataDescription from './TimelineDataDescription'; +import {HighlightContext} from '../Highlight'; +import {parseColor} from '../../utils/parseColor'; +// import TimelineDataDescription from './TimelineDataDescription'; TODO: +import {theme} from '../theme'; +import {EditOutlined} from '@ant-design/icons'; +import type {CheckboxChangeEvent} from 'antd/lib/checkbox'; + +const {Link} = Typography; + +type DataInspectorSetValue = (path: Array, val: any) => void; + +// Based on FIG UI Core, TODO: does that still makes sense? +const presetColors = Object.values({ + blue: '#4267b2', // Blue - Active-state nav glyphs, nav bars, links, buttons + green: '#42b72a', // Green - Confirmation, success, commerce and status + red: '#FC3A4B', // Red - Badges, error states + blueGrey: '#5f6673', // Blue Grey + slate: '#b9cad2', // Slate + aluminum: '#a3cedf', // Aluminum + seaFoam: '#54c7ec', // Sea Foam + teal: '#6bcebb', // Teal + lime: '#a3ce71', // Lime + lemon: '#fcd872', // Lemon + orange: '#f7923b', // Orange + tomato: '#fb724b', // Tomato - Tometo? Tomato. + cherry: '#f35369', // Cherry + pink: '#ec7ebd', // Pink + grape: '#8c72cb', // Grape +}); const NullValue = styled.span({ color: 'rgb(128, 128, 128)', @@ -32,13 +54,13 @@ const UndefinedValue = styled.span({ UndefinedValue.displayName = 'DataDescription:UndefinedValue'; const StringValue = styled.span({ - color: colors.cherryDark1, + color: '#e04c60', wordWrap: 'break-word', }); StringValue.displayName = 'DataDescription:StringValue'; const ColorValue = styled.span({ - color: colors.blueGrey, + color: '#5f6673', }); ColorValue.displayName = 'DataDescription:ColorValue'; @@ -48,7 +70,7 @@ const SymbolValue = styled.span({ SymbolValue.displayName = 'DataDescription:SymbolValue'; const NumberValue = styled.span({ - color: colors.tealDark1, + color: '#4dbba6', }); NumberValue.displayName = 'DataDescription:NumberValue'; @@ -57,9 +79,10 @@ const ColorBox = styled.span<{color: string}>((props) => ({ boxShadow: 'inset 0 0 1px rgba(0, 0, 0, 1)', display: 'inline-block', height: 12, - marginRight: 5, + marginRight: 4, verticalAlign: 'middle', width: 12, + borderRadius: 4, })); ColorBox.displayName = 'DataDescription:ColorBox'; @@ -185,7 +208,7 @@ class NumberTextEditor extends PureComponent<{ { @@ -315,13 +338,15 @@ class ColorEditor extends PureComponent<{ colorSet?: Array; commit: (opts: DescriptionCommitOptions) => void; }> { - onBlur = () => { - this.props.commit({ - clear: true, - keep: false, - value: this.props.value, - set: true, - }); + onBlur = (newVisibility: boolean) => { + if (!newVisibility) { + this.props.commit({ + clear: true, + keep: false, + value: this.props.value, + set: true, + }); + } }; onChange = ({ @@ -388,16 +413,11 @@ class ColorEditor extends PureComponent<{ } return ( - - - - {this.props.colorSet ? ( + + this.props.colorSet ? ( - )} - - + ) + }> + + + + ); } } @@ -505,65 +518,6 @@ class DataDescriptionPreview extends PureComponent<{ } } -function parseColor( - val: string | number, -): - | { - r: number; - g: number; - b: number; - a: number; - } - | undefined - | null { - if (typeof val === 'number') { - const a = ((val >> 24) & 0xff) / 255; - const r = (val >> 16) & 0xff; - const g = (val >> 8) & 0xff; - const b = val & 0xff; - return {a, b, g, r}; - } - if (typeof val !== 'string') { - return; - } - if (val[0] !== '#') { - return; - } - - // remove leading hash - val = val.slice(1); - - // only allow RGB and ARGB hex values - if (val.length !== 3 && val.length !== 6 && val.length !== 8) { - return; - } - - // split every 2 characters - const parts = val.match(/.{1,2}/g); - if (!parts) { - return; - } - - // get the alpha value - let a = 1; - - // extract alpha if passed AARRGGBB - if (val.length === 8) { - a = parseInt(parts.shift() || '0', 16) / 255; - } - - const size = val.length; - const [r, g, b] = parts.map((num) => { - if (size === 3) { - return parseInt(num + num, 16); - } else { - return parseInt(num, 16); - } - }); - - return {a, b, g, r}; -} - type Picker = { values: Set; selected: string; @@ -575,10 +529,10 @@ class DataDescriptionContainer extends PureComponent<{ editable: boolean; commit: (opts: DescriptionCommitOptions) => void; }> { - static contextType = _HighlightContext; // Replace with useHighlighter - context!: React.ContextType; + static contextType = HighlightContext; // Replace with useHighlighter + context!: React.ContextType; - onChangeCheckbox = (e: React.ChangeEvent) => { + onChangeCheckbox = (e: CheckboxChangeEvent) => { this.props.commit({ clear: true, keep: true, @@ -593,22 +547,24 @@ class DataDescriptionContainer extends PureComponent<{ switch (type) { case 'timeline': { - return ( - <> - { - this.props.commit({ - value: id, - keep: true, - clear: false, - set: true, - }); - }} - /> - - ); + return null; + // TODO: + // return ( + // <> + // { + // this.props.commit({ + // value: id, + // keep: true, + // clear: false, + // set: true, + // }); + // }} + // /> + // + // ); } case 'number': @@ -660,15 +616,15 @@ class DataDescriptionContainer extends PureComponent<{ case 'picker': { const picker: Picker = JSON.parse(val); - const options = [...picker.values].reduce((obj, value) => { - return {...obj, [value]: value}; - }, {}); return ( > 24) & 0xff) / 255; + const r = (val >> 16) & 0xff; + const g = (val >> 8) & 0xff; + const b = val & 0xff; + return {a, b, g, r}; + } + if (typeof val !== 'string') { + return; + } + if (val[0] !== '#') { + return; + } + + // remove leading hash + val = val.slice(1); + + // only allow RGB and ARGB hex values + if (val.length !== 3 && val.length !== 6 && val.length !== 8) { + return; + } + + // split every 2 characters + const parts = val.match(/.{1,2}/g); + if (!parts) { + return; + } + + // get the alpha value + let a = 1; + + // extract alpha if passed AARRGGBB + if (val.length === 8) { + a = parseInt(parts.shift() || '0', 16) / 255; + } + + const size = val.length; + const [r, g, b] = parts.map((num) => { + if (size === 3) { + return parseInt(num + num, 16); + } else { + return parseInt(num, 16); + } + }); + + return {a, b, g, r}; +}