diff --git a/packages/react-devtools-shared/src/__tests__/inspectedElement-test.js b/packages/react-devtools-shared/src/__tests__/inspectedElement-test.js index a73acd55bf46..2066fe5fa65f 100644 --- a/packages/react-devtools-shared/src/__tests__/inspectedElement-test.js +++ b/packages/react-devtools-shared/src/__tests__/inspectedElement-test.js @@ -583,6 +583,7 @@ describe('InspectedElement', () => { boolean_true={true} infinity={Infinity} minus_infinity={-Infinity} + minus_zero={-0} integer_zero={0} integer_one={1} float={1.23} @@ -606,6 +607,7 @@ describe('InspectedElement', () => { "integer_one": 1, "integer_zero": 0, "minus_infinity": -Infinity, + "minus_zero": -0, "nan": NaN, "string": "abc", "string_empty": "", diff --git a/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js b/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js index 62ba2e136087..862256228016 100644 --- a/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js +++ b/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js @@ -99,6 +99,7 @@ describe('InspectedElementContext', () => { boolean_true: true, infinity: Infinity, minus_infinity: -Infinity, + minus_zero: -0, integer_zero: 0, integer_one: 1, float: 1.23, @@ -130,6 +131,7 @@ describe('InspectedElementContext', () => { "integer_one": 1, "integer_zero": 0, "minus_infinity": -Infinity, + "minus_zero": -0, "nan": NaN, "string": "abc", "string_empty": "", diff --git a/packages/react-devtools-shared/src/__tests__/useEditableValue-test.js b/packages/react-devtools-shared/src/__tests__/useEditableValue-test.js index 61b538e2d2e6..1e8dfd5786a5 100644 --- a/packages/react-devtools-shared/src/__tests__/useEditableValue-test.js +++ b/packages/react-devtools-shared/src/__tests__/useEditableValue-test.js @@ -43,6 +43,24 @@ describe('useEditableValue', () => { expect(state.isValid).toBe(true); }); + it('should preserve -0 as an editable value', () => { + let state; + + function Example({value = -0}) { + const tuple = useEditableValue(value); + state = tuple[0]; + return null; + } + + act(() => render()); + + expect(state.editableValue).toEqual('-0'); + expect(Object.is(state.externalValue, -0)).toBe(true); + expect(Object.is(state.parsedValue, -0)).toBe(true); + expect(state.hasPendingChanges).toBe(false); + expect(state.isValid).toBe(true); + }); + it('should override editable state when external props are updated', () => { let state; diff --git a/packages/react-devtools-shared/src/devtools/utils.js b/packages/react-devtools-shared/src/devtools/utils.js index 43d3ea6c837b..182027d9dfaf 100644 --- a/packages/react-devtools-shared/src/devtools/utils.js +++ b/packages/react-devtools-shared/src/devtools/utils.js @@ -237,6 +237,8 @@ export function smartParse(value: any): any | void | number { return Infinity; case '-Infinity': return -Infinity; + case '-0': + return -0; case 'NaN': return NaN; case 'undefined': @@ -252,6 +254,8 @@ export function smartStringify(value: any): string { return 'NaN'; } else if (!Number.isFinite(value)) { return value > 0 ? 'Infinity' : '-Infinity'; + } else if (Object.is(value, -0)) { + return '-0'; } } else if (value === undefined) { return 'undefined'; diff --git a/packages/react-devtools-shared/src/devtools/views/Components/KeyValue.js b/packages/react-devtools-shared/src/devtools/views/Components/KeyValue.js index 89342ccffc1f..68d5b451cbf8 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/KeyValue.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/KeyValue.js @@ -273,6 +273,8 @@ export default function KeyValue({ displayValue = 'undefined'; } else if (isNaN(value)) { displayValue = 'NaN'; + } else if (Object.is(value, -0)) { + displayValue = '-0'; } let shouldDisplayValueAsLink = false; diff --git a/packages/react-devtools-shared/src/hydration.js b/packages/react-devtools-shared/src/hydration.js index 3670514ff4c6..3a304b518eb1 100644 --- a/packages/react-devtools-shared/src/hydration.js +++ b/packages/react-devtools-shared/src/hydration.js @@ -597,6 +597,7 @@ export function dehydrate( } case 'infinity': case '-infinity': + case '-0': case 'nan': case 'undefined': // Some values are lossy when sent through a WebSocket. @@ -707,6 +708,8 @@ export function hydrate( parent[last] = Infinity; } else if (value.type === '-infinity') { parent[last] = -Infinity; + } else if (value.type === '-0') { + parent[last] = -0; } else if (value.type === 'nan') { parent[last] = NaN; } else if (value.type === 'undefined') { diff --git a/packages/react-devtools-shared/src/utils.js b/packages/react-devtools-shared/src/utils.js index aa7ba33dc595..6093be428434 100644 --- a/packages/react-devtools-shared/src/utils.js +++ b/packages/react-devtools-shared/src/utils.js @@ -709,6 +709,7 @@ export type DataType = | 'html_element' | 'infinity' | '-infinity' + | '-0' | 'iterator' | 'opaque_iterator' | 'nan' @@ -767,6 +768,8 @@ export function getDataType(data: Object): DataType { return 'nan'; } else if (!Number.isFinite(data)) { return data > 0 ? 'infinity' : '-infinity'; + } else if (Object.is(data, -0)) { + return '-0'; } else { return 'number'; } @@ -1221,9 +1224,13 @@ export function formatDataForPreview( case 'number': case 'infinity': case '-infinity': + case '-0': case 'nan': case 'null': case 'undefined': + if (Object.is(data, -0)) { + return '-0'; + } return String(data); default: try {