diff --git a/frontend/package.json b/frontend/package.json index f9783a2bf4..612a71672a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -160,6 +160,7 @@ "packageManager": "yarn@4.9.2", "dependencies": { "chartjs-plugin-datalabels": "^2.2.0", - "json-2-csv": "^5.5.5" + "json-2-csv": "^5.5.5", + "json-diff-react": "^1.0.1" } } diff --git a/frontend/src/component/events/EventDiff/EventDiff.tsx b/frontend/src/component/events/EventDiff/EventDiff.tsx index a65f7e1591..c0b3c8dfd9 100644 --- a/frontend/src/component/events/EventDiff/EventDiff.tsx +++ b/frontend/src/component/events/EventDiff/EventDiff.tsx @@ -1,6 +1,8 @@ import { diff } from 'deep-diff'; -import { useTheme } from '@mui/system'; -import type { JSX, CSSProperties } from 'react'; +import { type JSX, type CSSProperties, useState, type FC, useId } from 'react'; +import { JsonDiffComponent, type JsonValue } from 'json-diff-react'; +import { Button, styled, useTheme } from '@mui/material'; +import { useUiFlag } from 'hooks/useUiFlag'; const DIFF_PREFIXES: Record = { A: ' ', @@ -17,10 +19,71 @@ interface IEventDiffResult { interface IEventDiffProps { entry: { data?: unknown; preData?: unknown }; + /** + * @deprecated remove with flag improvedJsonDiff + */ sort?: (a: IEventDiffResult, b: IEventDiffResult) => number; } -const EventDiff = ({ +const DiffStyles = styled('div')(({ theme }) => ({ + color: theme.palette.text.secondary, + fontFamily: 'monospace', + whiteSpace: 'pre', + fontSize: theme.typography.body2.fontSize, + + '.deletion, .addition': { + position: 'relative', + '::before': { + position: 'absolute', + left: 0, + top: 0, + marginLeft: '-10px', + }, + }, + + '.addition': { + color: theme.palette.eventLog.diffAdd, + '::before': { + content: '"+"', + }, + }, + '.deletion': { + color: theme.palette.eventLog.diffSub, + '::before': { + content: '"-"', + }, + }, +})); + +const NewEventDiff: FC = ({ entry }) => { + const [full, setFull] = useState(false); + const diffId = useId(); + + return ( + <> + + + + + + ); +}; + +const OldEventDiff: FC = ({ entry, sort = (a, b) => a.key.localeCompare(b.key), }: IEventDiffProps) => { @@ -113,11 +176,18 @@ const EventDiff = ({ } return ( - // biome-ignore lint/a11y/noNoninteractiveTabindex: -
+        
             {changes.length === 0 ? '(no changes)' : changes}
         
); }; +const EventDiff: FC = (props) => { + const useNewJsonDiff = useUiFlag('improvedJsonDiff'); + if (useNewJsonDiff) { + return ; + } + return ; +}; + export default EventDiff; diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index 45d5b90a11..a96e647889 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -90,6 +90,7 @@ export type UiFlags = { lifecycleMetrics?: boolean; createFlagDialogCache?: boolean; healthToTechDebt?: boolean; + improvedJsonDiff?: boolean; }; export interface IVersionInfo { diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 741648dd8e..90cedd256e 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1277,6 +1277,15 @@ __metadata: languageName: node linkType: hard +"@ewoudenberg/difflib@npm:^0.1.0": + version: 0.1.0 + resolution: "@ewoudenberg/difflib@npm:0.1.0" + dependencies: + heap: "npm:>= 0.2.0" + checksum: 10c0/3060807c91f39c5c1e5421fe51573bb2bffcab48cb32e9dcc69ce0d43e658dbf5959421382541012628ca6b842d252fc157a79f70cea8088a864572fec2bd094 + languageName: node + linkType: hard + "@exodus/schemasafe@npm:^1.0.0-rc.2": version: 1.3.0 resolution: "@exodus/schemasafe@npm:1.3.0" @@ -6079,6 +6088,13 @@ __metadata: languageName: node linkType: hard +"heap@npm:>= 0.2.0": + version: 0.2.7 + resolution: "heap@npm:0.2.7" + checksum: 10c0/341c5d51ae13dc8346c371a8a69c57c972fcb9a3233090d3dd5ba29d483d6b5b4e75492443cbfeacd46608bb30e6680f646ffb7a6205900221735587d07a79b6 + languageName: node + linkType: hard + "hoist-non-react-statics@npm:^3.3.1": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" @@ -6806,6 +6822,17 @@ __metadata: languageName: node linkType: hard +"json-diff-react@npm:^1.0.1": + version: 1.0.1 + resolution: "json-diff-react@npm:1.0.1" + dependencies: + "@ewoudenberg/difflib": "npm:^0.1.0" + react: "npm:^18.2.0" + react-dom: "npm:^18.2.0" + checksum: 10c0/085c395f61a8fb148d60c91bb279dbc071e76a89f96634da41df03b72ae365f916f3e5a12e77d6f4f64a680572849ae61d5a5853e1b83f9d7f31e7e8aa8e6de0 + languageName: node + linkType: hard + "json-parse-even-better-errors@npm:^2.3.0": version: 2.3.1 resolution: "json-parse-even-better-errors@npm:2.3.1" @@ -8567,7 +8594,7 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:18.3.1": +"react-dom@npm:18.3.1, react-dom@npm:^18.2.0": version: 18.3.1 resolution: "react-dom@npm:18.3.1" dependencies: @@ -8826,7 +8853,7 @@ __metadata: languageName: node linkType: hard -"react@npm:18.3.1": +"react@npm:18.3.1, react@npm:^18.2.0": version: 18.3.1 resolution: "react@npm:18.3.1" dependencies: @@ -10369,6 +10396,7 @@ __metadata: immer: "npm:9.0.21" jsdom: "npm:25.0.1" json-2-csv: "npm:^5.5.5" + json-diff-react: "npm:^1.0.1" lodash.clonedeep: "npm:4.5.0" lodash.isequal: "npm:^4.5.0" lodash.mapvalues: "npm:^4.6.0" diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index f184221c6a..9c5f89a0eb 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -61,6 +61,7 @@ export type IFlagKey = | 'lifecycleMetrics' | 'customMetrics' | 'createFlagDialogCache' + | 'improvedJsonDiff' | 'changeRequestApproverEmails'; export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>; @@ -286,6 +287,10 @@ const flags: IFlags = { process.env.UNLEASH_EXPERIMENTAL_CHANGE_REQUEST_APPROVER_EMAILS, false, ), + improvedJsonDiff: parseEnvVarBoolean( + process.env.UNLEASH_EXPERIMENTAL_IMPROVED_JSON_DIFF, + false, + ), }; export const defaultExperimentalOptions: IExperimentalOptions = { diff --git a/src/server-dev.ts b/src/server-dev.ts index 10c38c4f27..2d12dba8e5 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -55,6 +55,7 @@ process.nextTick(async () => { reportUnknownFlags: true, customMetrics: true, lifecycleMetrics: true, + improvedJsonDiff: true, }, }, authentication: {