Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions library/src/components/Highlight/Highlight.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useEffect, useRef } from 'react';

interface HighlightProps {
elementId: string;
duration?: number;
}

export const Highlight: React.FC<HighlightProps> = ({ elementId, duration = 2000 }) => {

Check failure on line 8 in library/src/components/Highlight/Highlight.tsx

View workflow job for this annotation

GitHub Actions / Test NodeJS PR - ubuntu-latest

Replace `·elementId,·duration·=·2000·` with `⏎··elementId,⏎··duration·=·2000,⏎`
const highlightRef = useRef<HTMLDivElement>(null);

useEffect(() => {
const element = document.getElementById(elementId);
if (!element || !highlightRef.current) return;

const rect = element.getBoundingClientRect();
const highlight = highlightRef.current;

// Position the highlight overlay
highlight.style.top = `${rect.top + window.scrollY}px`;
highlight.style.left = `${rect.left + window.scrollX}px`;
highlight.style.width = `${rect.width}px`;
highlight.style.height = `${rect.height}px`;
highlight.style.opacity = '1';

// Scroll the element into view
element.scrollIntoView({ behavior: 'smooth', block: 'center' });

// Fade out the highlight after the duration
const timeout = setTimeout(() => {
highlight.style.opacity = '0';
}, duration);

return () => clearTimeout(timeout);
}, [elementId, duration]);

return (
<div
ref={highlightRef}
style={{
position: 'absolute',
pointerEvents: 'none',
transition: 'opacity 0.5s ease-out',
opacity: '0',
backgroundColor: 'rgba(3, 169, 244, 0.1)',
border: '2px solid rgba(3, 169, 244, 0.4)',
borderRadius: '4px',
zIndex: 1000,
}}
/>
);
};

Check failure on line 51 in library/src/components/Highlight/Highlight.tsx

View workflow job for this annotation

GitHub Actions / Test NodeJS PR - ubuntu-latest

Insert `⏎`
1 change: 1 addition & 0 deletions library/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './Markdown';
export * from './Schema';
export * from './Tag';
export * from './Tags';
export * from './Highlight/Highlight';
33 changes: 27 additions & 6 deletions library/src/containers/AsyncApi/AsyncApi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { AsyncAPIDocumentInterface } from '@asyncapi/parser';

import AsyncApiStandalone from './Standalone';
import { Highlight } from '../../components/Highlight/Highlight';

import {
isFetchingSchemaInterface,
Expand All @@ -10,6 +11,7 @@
} from '../../types';
import { ConfigInterface } from '../../config';
import { SpecificationHelpers, Parser } from '../../helpers';
import { ChangeTracker } from '../../helpers/ChangeTracker';

export interface AsyncApiProps {
schema: PropsSchema;
Expand All @@ -19,12 +21,14 @@
interface AsyncAPIState {
asyncapi?: AsyncAPIDocumentInterface;
error?: ErrorObject;
highlightedElement?: string;
}

class AsyncApiComponent extends Component<AsyncApiProps, AsyncAPIState> {
state: AsyncAPIState = {
asyncapi: undefined,
error: undefined,
highlightedElement: undefined,
};

async componentDidMount() {
Expand All @@ -41,19 +45,36 @@
if (oldSchema !== newSchema) {
const { config } = this.props;
await this.parseSchema(newSchema, config?.parserOptions);

// Detect changes and update highlight
if (typeof oldSchema === 'string' && typeof newSchema === 'string') {
const changes = await ChangeTracker.detectChanges(oldSchema, newSchema);
if (changes.length > 0) {
// Get the first changed element to highlight
this.setState({ highlightedElement: changes[0].elementId });
}
}
}
}

render() {
const { schema, config } = this.props;
const { asyncapi, error } = this.state;
const { asyncapi, error, highlightedElement } = this.state;

return (
<AsyncApiStandalone
schema={asyncapi ?? schema}
config={config}
error={error}
/>
<>
<AsyncApiStandalone
schema={asyncapi ?? schema}
config={config}
error={error}
/>
{highlightedElement && (
<Highlight

Check failure on line 72 in library/src/containers/AsyncApi/AsyncApi.tsx

View workflow job for this annotation

GitHub Actions / Test NodeJS PR - ubuntu-latest

Replace `⏎············elementId={highlightedElement}⏎············duration={2000}⏎·········` with `·elementId={highlightedElement}·duration={2000}`
elementId={highlightedElement}
duration={2000}
/>
)}
</>
);
}

Expand Down
73 changes: 73 additions & 0 deletions library/src/helpers/ChangeTracker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Parser } from './parser';
import { ParserReturn } from '../types';

interface Change {
path: string[];
type: 'add' | 'remove' | 'modify';
elementId: string;
}

export class ChangeTracker {
private static async parseSchema(schema: string): Promise<ParserReturn | null> {

Check failure on line 11 in library/src/helpers/ChangeTracker.ts

View workflow job for this annotation

GitHub Actions / Test NodeJS PR - ubuntu-latest

Replace `schema:·string` with `⏎····schema:·string,⏎··`
try {
return await Parser.parse(schema);
} catch (e) {
return null;
}

Check warning on line 16 in library/src/helpers/ChangeTracker.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Handle this exception or don't catch it at all.

See more on https://sonarcloud.io/project/issues?id=asyncapi_asyncapi-react&issues=AZrA_xZIsPOyFzXT25NZ&open=AZrA_xZIsPOyFzXT25NZ&pullRequest=1177
}

static async detectChanges(oldSchema: string, newSchema: string): Promise<Change[]> {

Check failure on line 19 in library/src/helpers/ChangeTracker.ts

View workflow job for this annotation

GitHub Actions / Test NodeJS PR - ubuntu-latest

Replace `oldSchema:·string,·newSchema:·string` with `⏎····oldSchema:·string,⏎····newSchema:·string,⏎··`
const oldParsed = await this.parseSchema(oldSchema);
const newParsed = await this.parseSchema(newSchema);

Check failure on line 22 in library/src/helpers/ChangeTracker.ts

View workflow job for this annotation

GitHub Actions / Test NodeJS PR - ubuntu-latest

Delete `····`
if (!oldParsed?.asyncapi || !newParsed?.asyncapi) return [];

const oldDoc = oldParsed.asyncapi;
const newDoc = newParsed.asyncapi;
const changes: Change[] = [];

Check failure on line 28 in library/src/helpers/ChangeTracker.ts

View workflow job for this annotation

GitHub Actions / Test NodeJS PR - ubuntu-latest

Delete `····`
// Compare info section
if (JSON.stringify(oldDoc.info()) !== JSON.stringify(newDoc.info())) {
changes.push({
path: ['info'],
type: 'modify',
elementId: 'introduction'

Check failure on line 34 in library/src/helpers/ChangeTracker.ts

View workflow job for this annotation

GitHub Actions / Test NodeJS PR - ubuntu-latest

Insert `,`
});
}

// Compare servers
const oldServers = oldDoc.servers();
const newServers = newDoc.servers();
if (JSON.stringify(oldServers) !== JSON.stringify(newServers)) {
changes.push({
path: ['servers'],
type: 'modify',
elementId: 'servers'

Check failure on line 45 in library/src/helpers/ChangeTracker.ts

View workflow job for this annotation

GitHub Actions / Test NodeJS PR - ubuntu-latest

Insert `,`
});
}

// Compare channels
const oldChannels = oldDoc.channels();
const newChannels = newDoc.channels();
if (JSON.stringify(oldChannels) !== JSON.stringify(newChannels)) {
changes.push({
path: ['channels'],
type: 'modify',
elementId: 'operations'

Check failure on line 56 in library/src/helpers/ChangeTracker.ts

View workflow job for this annotation

GitHub Actions / Test NodeJS PR - ubuntu-latest

Insert `,`
});
}

// Compare schemas
const oldSchemas = oldDoc.components()?.schemas()?.all() || [];
const newSchemas = newDoc.components()?.schemas()?.all() || [];
if (JSON.stringify(oldSchemas) !== JSON.stringify(newSchemas)) {
changes.push({
path: ['components', 'schemas'],
type: 'modify',
elementId: 'schemas'
});
}

return changes;
}
}
Loading