Show component location for selected element in bottom/right of props panel (#17567)
* Show component location info for selected element in bottom/right of props panel * Moved RegExp declaration into function basedon PR feedback
Brian Vaughn committed
Dec 10, 2019 at 09:24 UTC
031a5aaffbc06f0ac115f1d814e43e4d63ae15d2
2 files changed
+75
packages/react-devtools-shared/src/devtools/views/Components/SelectedElement.css
+25
@@ -31,6 +31,31 @@
31
font-family: var(--font-family-sans);
32
}
33
34
+.Source {
35
+ padding: 0.25rem;
36
+ border-top: 1px solid var(--color-border);
37
+}
38
+
39
+.SourceHeaderRow {
40
+ display: flex;
41
+ align-items: center;
42
+}
43
+
44
+.SourceHeader {
45
+ flex: 1 1;
46
+ font-family: var(--font-family-sans);
47
+}
48
+
49
+.SourceOneLiner {
50
+ font-family: var(--font-family-monospace);
51
+ font-size: var(--font-size-monospace-normal);
52
+ white-space: nowrap;
53
+ overflow: hidden;
54
+ text-overflow: ellipsis;
55
+ max-width: 100%;
56
+ margin-left: 1rem;
57
+}
58
+
59
.Component,
60
.Owner {
61
color: var(--color-component-name);
packages/react-devtools-shared/src/devtools/views/Components/SelectedElement.js
+50
@@ -7,6 +7,7 @@
7
* @flow
8
*/
9
10
+import {copy} from 'clipboard-js';
11
import React, {useCallback, useContext} from 'react';
12
import {TreeDispatcherContext, TreeStateContext} from './TreeContext';
13
import {BridgeContext, StoreContext} from '../context';
@@ -272,6 +273,7 @@ function InspectedElementView({
273
hooks,
274
owners,
275
props,
276
+ source,
277
state,
278
} = inspectedElement;
279
@@ -403,6 +405,54 @@ function InspectedElementView({
405
))}
406
</div>
407
)}
408
+
409
+ {source !== null && (
410
+ <Source fileName={source.fileName} lineNumber={source.lineNumber} />
411
+ )}
412
+ </div>
413
+ );
414
+}
415
+
416
+// This function is based on packages/shared/describeComponentFrame.js
417
+function formatSourceForDisplay(fileName: string, lineNumber: string) {
418
+ const BEFORE_SLASH_RE = /^(.*)[\\\/]/;
419
+
420
+ let nameOnly = fileName.replace(BEFORE_SLASH_RE, '');
421
+
422
+ // In DEV, include code for a common special case:
423
+ // prefer "folder/index.js" instead of just "index.js".
424
+ if (/^index\./.test(nameOnly)) {
425
+ const match = fileName.match(BEFORE_SLASH_RE);
426
+ if (match) {
427
+ const pathBeforeSlash = match[1];
428
+ if (pathBeforeSlash) {
429
+ const folderName = pathBeforeSlash.replace(BEFORE_SLASH_RE, '');
430
+ nameOnly = folderName + '/' + nameOnly;
431
+ }
432
+ }
433
+ }
434
+
435
+ return `${nameOnly}:${lineNumber}`;
436
+}
437
+
438
+type SourceProps = {|
439
+ fileName: string,
440
+ lineNumber: string,
441
+|};
442
+
443
+function Source({fileName, lineNumber}: SourceProps) {
444
+ const handleCopy = () => copy(`${fileName}:${lineNumber}`);
445
+ return (
446
+ <div className={styles.Source}>
447
+ <div className={styles.SourceHeaderRow}>
448
+ <div className={styles.SourceHeader}>source</div>
449
+ <Button onClick={handleCopy} title="Copy to clipboard">
450
+ <ButtonIcon type="copy" />
451
+ </Button>
452
+ </div>
453
+ <div className={styles.SourceOneLiner}>
454
+ {formatSourceForDisplay(fileName, lineNumber)}
455
+ </div>
456
</div>
457
);
458
}