main
js 123 lines 3.41 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import {copy} from 'clipboard-js';
11 import * as React from 'react';
12 import {OptionsContext} from '../context';
13 import Button from '../Button';
14 import ButtonIcon from '../ButtonIcon';
15 import KeyValue from './KeyValue';
16 import NewKeyValue from './NewKeyValue';
17 import {alphaSortEntries, serializeDataForCopy} from '../utils';
18 import Store from '../../store';
19 import styles from './InspectedElementSharedStyles.css';
20 import {
21 ElementTypeClass,
22 ElementTypeSuspense,
23 ElementTypeActivity,
24 } from 'react-devtools-shared/src/frontend/types';
25 import {withPermissionsCheck} from 'react-devtools-shared/src/frontend/utils/withPermissionsCheck';
26
27 import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
28 import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
29 import type {Element} from 'react-devtools-shared/src/frontend/types';
30
31 type Props = {
32 bridge: FrontendBridge,
33 element: Element,
34 inspectedElement: InspectedElement,
35 store: Store,
36 };
37
38 export default function InspectedElementPropsTree({
39 bridge,
40 element,
41 inspectedElement,
42 store,
43 }: Props): React.Node {
44 const {readOnly} = React.useContext(OptionsContext);
45
46 const {
47 canEditFunctionProps,
48 canEditFunctionPropsDeletePaths,
49 canEditFunctionPropsRenamePaths,
50 props,
51 type,
52 } = inspectedElement;
53
54 const canDeletePaths =
55 type === ElementTypeClass || canEditFunctionPropsDeletePaths;
56 const canEditValues =
57 !readOnly &&
58 (type === ElementTypeClass || canEditFunctionProps) &&
59 // Make it read-only for Suspense to make it a bit cleaner. It's not
60 // useful to edit children anyway.
61 type !== ElementTypeSuspense &&
62 type !== ElementTypeActivity;
63 const canRenamePaths =
64 type === ElementTypeClass || canEditFunctionPropsRenamePaths;
65
66 // Skip the section for null props.
67 if (props == null) {
68 return null;
69 }
70
71 const entries = Object.entries(props);
72 entries.sort(alphaSortEntries);
73 const isEmpty = entries.length === 0;
74
75 const handleCopy = withPermissionsCheck(
76 {permissions: ['clipboardWrite']},
77 () => copy(serializeDataForCopy(props)),
78 );
79
80 return (
81 <div data-testname="InspectedElementPropsTree">
82 <div className={styles.HeaderRow}>
83 <div className={styles.Header}>props</div>
84 {!isEmpty && (
85 <Button onClick={handleCopy} title="Copy to clipboard">
86 <ButtonIcon type="copy" />
87 </Button>
88 )}
89 </div>
90 {!isEmpty &&
91 entries.map(([name, value]) => (
92 <KeyValue
93 key={name}
94 alphaSort={true}
95 bridge={bridge}
96 canDeletePaths={canDeletePaths}
97 canEditValues={canEditValues}
98 canRenamePaths={canRenamePaths}
99 depth={1}
100 element={element}
101 hidden={false}
102 inspectedElement={inspectedElement}
103 name={name}
104 path={[name]}
105 pathRoot="props"
106 store={store}
107 value={value}
108 />
109 ))}
110 {canEditValues && (
111 <NewKeyValue
112 bridge={bridge}
113 depth={0}
114 hidden={false}
115 inspectedElement={inspectedElement}
116 path={[]}
117 store={store}
118 type="props"
119 />
120 )}
121 </div>
122 );
123 }