Skip to content

Commit

Permalink
Improved inspected element props with inline previews (#17579)
Browse files Browse the repository at this point in the history
* Improved inspected element props with inline previews

This mimics the inline preview shown by the brower console and dramatically improves the UX when inspecting deep values. I also updated tests to add more coverage for this new functionality.

* Cleaned up the DataView vs typed array check

* Added early bailouts to DevTools when generating preview strings for iterables/objects/arrays, to avoid doing unnecessary work
  • Loading branch information
Brian Vaughn authored Dec 12, 2019
1 parent 2afeebd commit 12c0004
Show file tree
Hide file tree
Showing 9 changed files with 508 additions and 176 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,11 @@ exports[`InspectedElementContext should support complex data types: 1: Inspected
"hooks": null,
"props": {
"array_buffer": {},
"array_of_arrays": [
{}
],
"big_int": {},
"data_view": {},
"date": {},
"fn": {},
"html_element": {},
Expand All @@ -503,6 +507,9 @@ exports[`InspectedElementContext should support complex data types: 1: Inspected
"0": {},
"1": {}
},
"object_of_objects": {
"inner": {}
},
"react_element": {},
"set": {
"0": "abc",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,13 +511,19 @@ describe('InspectedElementContext', () => {

const Example = () => null;

const arrayOfArrays = [[['abc', 123, true], []]];
const div = document.createElement('div');
const exampleFunction = () => {};
const setShallow = new Set(['abc', 123]);
const mapShallow = new Map([['name', 'Brian'], ['food', 'sushi']]);
const setOfSets = new Set([new Set(['a', 'b', 'c']), new Set([1, 2, 3])]);
const mapOfMaps = new Map([['first', mapShallow], ['second', mapShallow]]);
const objectOfObjects = {
inner: {string: 'abc', number: 213, boolean: true},
};
const typedArray = Int8Array.from([100, -100, 0]);
const arrayBuffer = typedArray.buffer;
const dataView = new DataView(arrayBuffer);
const immutableMap = Immutable.fromJS({
a: [{hello: 'there'}, 'fixed', true],
b: 123,
Expand All @@ -531,15 +537,18 @@ describe('InspectedElementContext', () => {
await utils.actAsync(() =>
ReactDOM.render(
<Example
array_buffer={typedArray.buffer}
array_buffer={arrayBuffer}
array_of_arrays={arrayOfArrays}
// eslint-disable-next-line no-undef
big_int={BigInt(123)}
date={new Date()}
data_view={dataView}
date={new Date(123)}
fn={exampleFunction}
html_element={div}
immutable={immutableMap}
map={mapShallow}
map_of_maps={mapOfMaps}
object_of_objects={objectOfObjects}
react_element={<span />}
set={setShallow}
set_of_sets={setOfSets}
Expand Down Expand Up @@ -579,13 +588,16 @@ describe('InspectedElementContext', () => {

const {
array_buffer,
array_of_arrays,
big_int,
data_view,
date,
fn,
html_element,
immutable,
map,
map_of_maps,
object_of_objects,
react_element,
set,
set_of_sets,
Expand All @@ -597,54 +609,111 @@ describe('InspectedElementContext', () => {
expect(array_buffer[meta.inspectable]).toBe(false);
expect(array_buffer[meta.name]).toBe('ArrayBuffer');
expect(array_buffer[meta.type]).toBe('array_buffer');
expect(array_buffer[meta.preview_short]).toBe('ArrayBuffer(3)');
expect(array_buffer[meta.preview_long]).toBe('ArrayBuffer(3)');

expect(array_of_arrays[0][meta.size]).toBe(2);
expect(array_of_arrays[0][meta.inspectable]).toBe(true);
expect(array_of_arrays[0][meta.name]).toBe('Array');
expect(array_of_arrays[0][meta.type]).toBe('array');
expect(array_of_arrays[0][meta.preview_long]).toBe('[Array(3), Array(0)]');
expect(array_of_arrays[0][meta.preview_short]).toBe('Array(2)');

expect(big_int[meta.inspectable]).toBe(false);
expect(big_int[meta.name]).toBe('123');
expect(big_int[meta.type]).toBe('bigint');
expect(big_int[meta.preview_long]).toBe('123n');
expect(big_int[meta.preview_short]).toBe('123n');

expect(data_view[meta.size]).toBe(3);
expect(data_view[meta.inspectable]).toBe(false);
expect(data_view[meta.name]).toBe('DataView');
expect(data_view[meta.type]).toBe('data_view');
expect(data_view[meta.preview_long]).toBe('DataView(3)');
expect(data_view[meta.preview_short]).toBe('DataView(3)');

expect(date[meta.inspectable]).toBe(false);
expect(date[meta.type]).toBe('date');
expect(date[meta.preview_long]).toBe(
'Wed Dec 31 1969 16:00:00 GMT-0800 (Pacific Standard Time)',
);
expect(date[meta.preview_short]).toBe(
'Wed Dec 31 1969 16:00:00 GMT-0800 (Pacific Standard Time)',
);

expect(fn[meta.inspectable]).toBe(false);
expect(fn[meta.name]).toBe('exampleFunction');
expect(fn[meta.type]).toBe('function');
expect(fn[meta.preview_long]).toBe('exampleFunction');
expect(fn[meta.preview_short]).toBe('exampleFunction');

expect(html_element[meta.inspectable]).toBe(false);
expect(html_element[meta.name]).toBe('DIV');
expect(html_element[meta.type]).toBe('html_element');
expect(html_element[meta.preview_long]).toBe('<div />');
expect(html_element[meta.preview_short]).toBe('<div />');

expect(immutable[meta.inspectable]).toBeUndefined(); // Complex type
expect(immutable[meta.name]).toBe('Map');
expect(immutable[meta.type]).toBe('iterator');
expect(immutable[meta.preview_long]).toBe(
'Map(3) {"a" => List(3), "b" => 123, "c" => Map(2)}',
);
expect(immutable[meta.preview_short]).toBe('Map(3)');

expect(map[meta.inspectable]).toBeUndefined(); // Complex type
expect(map[meta.name]).toBe('Map');
expect(map[meta.type]).toBe('iterator');
expect(map[0][meta.type]).toBe('array');
expect(map[meta.preview_long]).toBe(
'Map(2) {"name" => "Brian", "food" => "sushi"}',
);
expect(map[meta.preview_short]).toBe('Map(2)');

expect(map_of_maps[meta.inspectable]).toBeUndefined(); // Complex type
expect(map_of_maps[meta.name]).toBe('Map');
expect(map_of_maps[meta.type]).toBe('iterator');
expect(map_of_maps[0][meta.type]).toBe('array');
expect(map_of_maps[meta.preview_long]).toBe(
'Map(2) {"first" => Map(2), "second" => Map(2)}',
);
expect(map_of_maps[meta.preview_short]).toBe('Map(2)');

expect(object_of_objects.inner[meta.size]).toBe(3);
expect(object_of_objects.inner[meta.inspectable]).toBe(true);
expect(object_of_objects.inner[meta.name]).toBe('');
expect(object_of_objects.inner[meta.type]).toBe('object');
expect(object_of_objects.inner[meta.preview_long]).toBe(
'{boolean: true, number: 213, string: "abc"}',
);
expect(object_of_objects.inner[meta.preview_short]).toBe('{…}');

expect(react_element[meta.inspectable]).toBe(false);
expect(react_element[meta.name]).toBe('span');
expect(react_element[meta.type]).toBe('react_element');
expect(react_element[meta.preview_long]).toBe('<span />');
expect(react_element[meta.preview_short]).toBe('<span />');

expect(set[meta.inspectable]).toBeUndefined(); // Complex type
expect(set[meta.name]).toBe('Set');
expect(set[meta.type]).toBe('iterator');
expect(set[0]).toBe('abc');
expect(set[1]).toBe(123);
expect(set[meta.preview_long]).toBe('Set(2) {"abc", 123}');
expect(set[meta.preview_short]).toBe('Set(2)');

expect(set_of_sets[meta.inspectable]).toBeUndefined(); // Complex type
expect(set_of_sets[meta.name]).toBe('Set');
expect(set_of_sets[meta.type]).toBe('iterator');
expect(set_of_sets['0'][meta.inspectable]).toBe(true);
expect(set_of_sets[meta.preview_long]).toBe('Set(2) {Set(3), Set(3)}');
expect(set_of_sets[meta.preview_short]).toBe('Set(2)');

expect(symbol[meta.inspectable]).toBe(false);
expect(symbol[meta.name]).toBe('Symbol(symbol)');
expect(symbol[meta.type]).toBe('symbol');
expect(symbol[meta.preview_long]).toBe('Symbol(symbol)');
expect(symbol[meta.preview_short]).toBe('Symbol(symbol)');

expect(typed_array[meta.inspectable]).toBeUndefined(); // Complex type
expect(typed_array[meta.size]).toBe(3);
Expand All @@ -653,6 +722,8 @@ describe('InspectedElementContext', () => {
expect(typed_array[0]).toBe(100);
expect(typed_array[1]).toBe(-100);
expect(typed_array[2]).toBe(0);
expect(typed_array[meta.preview_long]).toBe('Int8Array(3) [100, -100, 0]');
expect(typed_array[meta.preview_short]).toBe('Int8Array(3)');

done();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ Object {
"hooks": null,
"props": {
"array_buffer": {},
"array_of_arrays": [
{}
],
"big_int": {},
"data_view": {},
"date": {},
"fn": {},
"html_element": {},
Expand All @@ -144,6 +148,9 @@ Object {
"0": {},
"1": {}
},
"object_of_objects": {
"inner": {}
},
"react_element": {},
"set": {
"0": "abc",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,19 @@ describe('InspectedElementContext', () => {

const Example = () => null;

const arrayOfArrays = [[['abc', 123, true], []]];
const div = document.createElement('div');
const exampleFunction = () => {};
const setShallow = new Set(['abc', 123]);
const mapShallow = new Map([['name', 'Brian'], ['food', 'sushi']]);
const setOfSets = new Set([new Set(['a', 'b', 'c']), new Set([1, 2, 3])]);
const mapOfMaps = new Map([['first', mapShallow], ['second', mapShallow]]);
const objectOfObjects = {
inner: {string: 'abc', number: 213, boolean: true},
};
const typedArray = Int8Array.from([100, -100, 0]);
const arrayBuffer = typedArray.buffer;
const dataView = new DataView(arrayBuffer);
const immutableMap = Immutable.fromJS({
a: [{hello: 'there'}, 'fixed', true],
b: 123,
Expand All @@ -166,15 +172,18 @@ describe('InspectedElementContext', () => {
act(() =>
ReactDOM.render(
<Example
array_buffer={typedArray.buffer}
array_buffer={arrayBuffer}
array_of_arrays={arrayOfArrays}
// eslint-disable-next-line no-undef
big_int={BigInt(123)}
date={new Date()}
data_view={dataView}
date={new Date(123)}
fn={exampleFunction}
html_element={div}
immutable={immutableMap}
map={mapShallow}
map_of_maps={mapOfMaps}
object_of_objects={objectOfObjects}
react_element={<span />}
set={setShallow}
set_of_sets={setOfSets}
Expand All @@ -192,13 +201,16 @@ describe('InspectedElementContext', () => {

const {
array_buffer,
array_of_arrays,
big_int,
data_view,
date,
fn,
html_element,
immutable,
map,
map_of_maps,
object_of_objects,
react_element,
set,
set_of_sets,
Expand All @@ -210,11 +222,25 @@ describe('InspectedElementContext', () => {
expect(array_buffer[meta.inspectable]).toBe(false);
expect(array_buffer[meta.name]).toBe('ArrayBuffer');
expect(array_buffer[meta.type]).toBe('array_buffer');
expect(array_buffer[meta.preview_short]).toBe('ArrayBuffer(3)');
expect(array_buffer[meta.preview_long]).toBe('ArrayBuffer(3)');

expect(array_of_arrays[0][meta.size]).toBe(2);
expect(array_of_arrays[0][meta.inspectable]).toBe(true);
expect(array_of_arrays[0][meta.name]).toBe('Array');
expect(array_of_arrays[0][meta.type]).toBe('array');
expect(array_of_arrays[0][meta.preview_long]).toBe('[Array(3), Array(0)]');
expect(array_of_arrays[0][meta.preview_short]).toBe('Array(2)');

expect(big_int[meta.inspectable]).toBe(false);
expect(big_int[meta.name]).toBe('123');
expect(big_int[meta.type]).toBe('bigint');

expect(data_view[meta.size]).toBe(3);
expect(data_view[meta.inspectable]).toBe(false);
expect(data_view[meta.name]).toBe('DataView');
expect(data_view[meta.type]).toBe('data_view');

expect(date[meta.inspectable]).toBe(false);
expect(date[meta.type]).toBe('date');

Expand All @@ -240,6 +266,15 @@ describe('InspectedElementContext', () => {
expect(map_of_maps[meta.type]).toBe('iterator');
expect(map_of_maps[0][meta.type]).toBe('array');

expect(object_of_objects.inner[meta.size]).toBe(3);
expect(object_of_objects.inner[meta.inspectable]).toBe(true);
expect(object_of_objects.inner[meta.name]).toBe('');
expect(object_of_objects.inner[meta.type]).toBe('object');
expect(object_of_objects.inner[meta.preview_long]).toBe(
'{boolean: true, number: 213, string: "abc"}',
);
expect(object_of_objects.inner[meta.preview_short]).toBe('{…}');

expect(react_element[meta.inspectable]).toBe(false);
expect(react_element[meta.name]).toBe('span');
expect(react_element[meta.type]).toBe('react_element');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export default function KeyValue({
} else {
if (Array.isArray(value)) {
const hasChildren = value.length > 0;
const displayName = getMetaValueLabel(value);

children = value.map((innerValue, index) => (
<KeyValue
Expand Down Expand Up @@ -164,10 +165,7 @@ export default function KeyValue({
onClick={hasChildren ? toggleIsOpen : undefined}>
{name}
</span>
<span>
Array{' '}
{hasChildren ? '' : <span className={styles.Empty}>(empty)</span>}
</span>
<span className={styles.Value}>{displayName}</span>
</div>,
);
} else {
Expand All @@ -180,9 +178,7 @@ export default function KeyValue({
}

const hasChildren = entries.length > 0;
const displayName = value.hasOwnProperty(meta.unserializable)
? getMetaValueLabel(value)
: 'Object';
const displayName = getMetaValueLabel(value);

let areChildrenReadOnly = isReadOnly || !!value[meta.readonly];
children = entries.map<Element<any>>(([key, keyValue]) => (
Expand Down Expand Up @@ -215,10 +211,7 @@ export default function KeyValue({
onClick={hasChildren ? toggleIsOpen : undefined}>
{name}
</span>
<span>
{`${displayName || ''} `}
{hasChildren ? '' : <span className={styles.Empty}>(empty)</span>}
</span>
<span className={styles.Value}>{displayName}</span>
</div>,
);
}
Expand Down
Loading

0 comments on commit 12c0004

Please sign in to comment.