Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sticky notes #7277

Merged
merged 42 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
bb291a7
Create StickyNotes DB entity
philemone Oct 28, 2024
460b354
Add PoC httpApiService for StickyNotes - add slick migration
philemone Nov 4, 2024
a56fd32
Add put and delete endpoints, use value classes for ID in stickyNotes…
philemone Nov 6, 2024
d5800c0
Add StickyNote PoC to FE
philemone Nov 8, 2024
8dec8fb
Resize and update StickyNotes
philemone Nov 12, 2024
d986e5a
Handle note removal
philemone Nov 13, 2024
b7b4dcd
Remove some unused fragments
philemone Nov 13, 2024
a0f4be4
Update openApi definition
philemone Nov 13, 2024
c7d0be3
Resize stickyNote without visual-lag
philemone Nov 14, 2024
4775cc3
Disable stickyNotes when scenario is not saved
philemone Nov 14, 2024
45004f1
Show/hide tools on graph actions
philemone Nov 14, 2024
7662eab
Edit stickyNote on graph
philemone Nov 14, 2024
f4d9203
Fix some suggestions made by rabbit
philemone Nov 15, 2024
21cdf9b
Update openApi definitions
philemone Nov 15, 2024
af02ce1
Add white characters to textarea in stickyNote markdown editor
philemone Nov 15, 2024
4a234b5
Allow focus to stay in markdown editor
philemone Nov 19, 2024
c7631c1
Allow switch viewer to editor witgh left mouse click
philemone Nov 19, 2024
8db8873
Add stickyNotes length and count validation, add stickyNotes configur…
philemone Nov 19, 2024
85b4d7b
Remove node specific code from StickyNotePreview, update openApi defs
philemone Nov 20, 2024
43fe6e0
Add some fixes, improve types, add max width and height for stickyNote
philemone Nov 20, 2024
b303ff2
Add STICKY_NOTE_CONSTRAINTS with config values
philemone Nov 20, 2024
401b689
Reuse common code, restore default color, remove duplicated update me…
philemone Nov 20, 2024
774f9bb
Restore CSS class, remove unused imports
philemone Nov 20, 2024
4c6dfce
Remove stickyNotePanel, add stickyNote to creatos panel
philemone Nov 20, 2024
38183a3
Update cypress test
philemone Nov 21, 2024
251b928
Add Changelog and migrationGuide entries
philemone Nov 25, 2024
c698317
Updated snapshots (#7275)
github-actions[bot] Dec 3, 2024
ffe8c53
remove space
philemone Dec 3, 2024
a3de8b6
Remove DOMpurify and use XSS instead
philemone Dec 9, 2024
7e4c944
Remove StickyNotePreview from ComponentPreview
philemone Dec 10, 2024
26231da
Rename stickyNotes panel to sticky notes
philemone Dec 12, 2024
3a8ef79
Fix markdown preview
philemone Dec 16, 2024
797294f
Change shape of stickyNote, fix 'changed' detection for notes
philemone Dec 23, 2024
b0255fb
Add notifications to graph, prevent from making changes to old sticky…
philemone Dec 23, 2024
18b62d4
Revert fix in error notification msg
philemone Dec 27, 2024
d59dd81
Fix warn messages
philemone Dec 27, 2024
4254548
Add strict type for color #xxxxxx, remove unnecessary generated noteI…
philemone Jan 9, 2025
5858284
Update pointer, add cypress tests, remove todo from uncomplete method…
philemone Jan 9, 2025
52d751a
Add regex in sticky note tests for note id
philemone Jan 9, 2025
d4229c6
Change remove tool color and margins, update cypress tests
philemone Jan 13, 2025
599ae11
update cypress tests
philemone Jan 13, 2025
a5ec18b
Add maxDiffThreshold to flaky cypress test
philemone Jan 13, 2025
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion designer/client/cypress/e2e/components.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ describe("Components list", () => {
cy.matchQuery("?TEXT=xxx");
cy.viewport(1600, 500);
cy.wait(500); //ensure "loading" mask is hidden
cy.get("#app-container>main").matchImage();
cy.get("#app-container>main").matchImage({ maxDiffThreshold: 0.01 });
});

it("should allow filtering by processing mode", () => {
Expand Down
1 change: 1 addition & 0 deletions designer/client/cypress/e2e/creatorToolbar.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ describe("Creator toolbar", () => {
cy.contains(/^types$/i).click();
cy.contains(/^services$/i).click();
cy.contains(/^sinks$/i).click();
cy.contains(/^sticky notes$/i).click();
cy.reload();
cy.get("@toolbar").matchImage();
cy.get("@toolbar").find("input").type("var");
Expand Down
65 changes: 65 additions & 0 deletions designer/client/cypress/e2e/stickyNotes.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
describe("Sticky notes", () => {
const seed = "stickyNotes";

before(() => {
cy.deleteAllTestProcesses({ filter: seed, force: true });
});

beforeEach(() => {
cy.visitNewProcess(seed, "stickyNotes");
});

const screenshotOptions: Cypress.MatchImageOptions = {
screenshotConfig: { clip: { x: 0, y: 0, width: 1400, height: 600 } },
};

it("should allow to drag sticky note", () => {
cy.layoutScenario();
cy.contains(/^sticky notes$/i)
.should("exist")
.scrollIntoView();
cy.get("[data-testid='component:sticky note']")
.should("be.visible")
.drag("#nk-graph-main", {
target: {
x: 600,
y: 300,
},
force: true,
});

cy.get("[data-testid=graphPage]").matchImage(screenshotOptions);
});

it("should add text to note and display it as markdown", () => {
cy.layoutScenario();
cy.contains(/^sticky notes$/i)
.should("exist")
.scrollIntoView();
cy.get("[data-testid='component:sticky note']")
.should("be.visible")
.drag("#nk-graph-main", {
target: {
x: 600,
y: 300,
},
force: true,
});
cy.get(".sticky-note-content").dblclick();
cy.get(".sticky-note-content textarea").type("# Title\n- p1\n- p2\n\n[link](href)");
cy.get("[model-id='request']").click();
cy.get("[data-testid=graphPage]").matchImage(screenshotOptions);
});

it("should disable sticky note when scenario is not saved", () => {
cy.layoutScenario();
cy.contains(/^sticky notes$/i)
.should("exist")
.scrollIntoView();

cy.dragNode("request", { x: 600, y: 300 });

cy.get("[data-testid='component:sticky note']").should("have.class", "tool disabled");
cy.get("[data-testid=graphPage]").matchImage(screenshotOptions);
});
});
58 changes: 58 additions & 0 deletions designer/client/cypress/fixtures/stickyNotes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"metaData": {
"id": "sticky",
"additionalFields": {
"description": null,
"properties": {
"inputSchema": "{}",
"outputSchema": "{}",
"slug": "sticky"
},
"metaDataType": "RequestResponseMetaData",
"showDescription": false
}
},
"nodes": [
{
"id": "request",
"ref": {
"typ": "request",
"parameters": []
},
"additionalFields": {
"description": null,
"layoutData": {
"x": 0,
"y": 0
}
},
"type": "Source"
},
{
"id": "response",
"ref": {
"typ": "response",
"parameters": [
{
"name": "Raw editor",
"expression": {
"language": "spel",
"expression": "false"
}
}
]
},
"endResult": null,
"isDisabled": null,
"additionalFields": {
"description": null,
"layoutData": {
"x": 0,
"y": 180
}
},
"type": "Sink"
}
],
"additionalBranches": []
}
2 changes: 2 additions & 0 deletions designer/client/src/actions/actionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export type ActionTypes =
| "DELETE_NODES"
| "NODES_CONNECTED"
| "NODES_DISCONNECTED"
| "STICKY_NOTES_UPDATED"
| "STICKY_NOTE_DELETED"
| "VALIDATION_RESULT"
| "COPY_SELECTION"
| "CUT_SELECTION"
Expand Down
7 changes: 7 additions & 0 deletions designer/client/src/actions/nk/assignSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ export type FeaturesSettings = {
redirectAfterArchive: boolean;
usageStatisticsReports: UsageStatisticsReports;
surveySettings: SurveySettings;
stickyNotesSettings: StickyNotesSettings;
};

export type StickyNotesSettings = {
maxContentLength: number;
maxNotesCount: number;
enabled: boolean;
};
philemone marked this conversation as resolved.
Show resolved Hide resolved

export type TestDataSettings = {
Expand Down
43 changes: 43 additions & 0 deletions designer/client/src/actions/nk/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { getProcessDefinitionData } from "../../reducers/selectors/settings";
import { ProcessDefinitionData, ScenarioGraph } from "../../types";
import { ThunkAction } from "../reduxTypes";
import HttpService from "./../../http/HttpService";
import { layoutChanged, Position } from "./ui/layout";
import { flushSync } from "react-dom";
import { Dimensions, StickyNote } from "../../common/StickyNote";

export type ScenarioActions =
| { type: "CORRECT_INVALID_SCENARIO"; processDefinitionData: ProcessDefinitionData }
Expand All @@ -17,6 +20,7 @@ export function fetchProcessToDisplay(processName: ProcessName, versionId?: Proc

return HttpService.fetchProcessDetails(processName, versionId).then((response) => {
dispatch(displayTestCapabilities(processName, response.data.scenarioGraph));
dispatch(fetchStickyNotesForScenario(processName, response.data.processVersionId));
dispatch({
type: "DISPLAY_PROCESS",
scenario: response.data,
Expand Down Expand Up @@ -56,6 +60,45 @@ export function displayTestCapabilities(processName: ProcessName, scenarioGraph:
);
}

const refreshStickyNotes = (dispatch, scenarioName: string, scenarioVersionId: number) => {
return HttpService.getStickyNotes(scenarioName, scenarioVersionId).then((stickyNotes) => {
flushSync(() => {
dispatch({ type: "STICKY_NOTES_UPDATED", stickyNotes: stickyNotes.data });
dispatch(layoutChanged());
});
});
};

export function fetchStickyNotesForScenario(scenarioName: string, scenarioVersionId: number): ThunkAction {
return (dispatch) => refreshStickyNotes(dispatch, scenarioName, scenarioVersionId);
}
philemone marked this conversation as resolved.
Show resolved Hide resolved

export function stickyNoteUpdated(scenarioName: string, scenarioVersionId: number, stickyNote: StickyNote): ThunkAction {
return (dispatch) => {
HttpService.updateStickyNote(scenarioName, scenarioVersionId, stickyNote).then((_) => {
refreshStickyNotes(dispatch, scenarioName, scenarioVersionId);
Dzuming marked this conversation as resolved.
Show resolved Hide resolved
});
};
}

export function stickyNoteDeleted(scenarioName: string, stickyNoteId: number): ThunkAction {
return (dispatch) => {
HttpService.deleteStickyNote(scenarioName, stickyNoteId).then(() => {
flushSync(() => {
dispatch({ type: "STICKY_NOTE_DELETED", stickyNoteId });
});
});
};
}

export function stickyNoteAdded(scenarioName: string, scenarioVersionId: number, position: Position, dimensions: Dimensions): ThunkAction {
return (dispatch) => {
HttpService.addStickyNote(scenarioName, scenarioVersionId, position, dimensions).then((_) => {
refreshStickyNotes(dispatch, scenarioName, scenarioVersionId);
});
};
}
philemone marked this conversation as resolved.
Show resolved Hide resolved

export function displayCurrentProcessVersion(processName: ProcessName) {
return fetchProcessToDisplay(processName);
}
Expand Down
8 changes: 8 additions & 0 deletions designer/client/src/actions/notificationActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import Notifications from "react-notification-system-redux";
import CheckCircleOutlinedIcon from "@mui/icons-material/CheckCircleOutlined";
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
import WarningAmberOutlinedIcon from "@mui/icons-material/WarningAmberOutlined";
import Notification from "../components/notifications/Notification";
import { Action } from "./reduxTypes";

Expand All @@ -25,3 +26,10 @@ export function info(message: string): Action {
children: <Notification type={"info"} icon={<InfoOutlinedIcon />} message={message} />,
});
}

export function warn(message: string): Action {
return Notifications.warning({
autoDismiss: 10,
children: <Notification type={"warning"} icon={<WarningAmberOutlinedIcon />} message={message} />,
});
}
3 changes: 3 additions & 0 deletions designer/client/src/assets/json/nodeAttributes.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
"Aggregate": {
"name": "Aggregate"
},
"StickyNote": {
"name": "StickyNote"
},
"CustomNode": {
"name": "CustomNode"
},
Expand Down
16 changes: 16 additions & 0 deletions designer/client/src/common/StickyNote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { LayoutData } from "../types";

export type Dimensions = { width: number; height: number };
export type ColorValueHex = `#${string}`;

export interface StickyNote {
id?: string;
noteId: number;
content: string;
layoutData: LayoutData;
dimensions: Dimensions;
color: ColorValueHex;
targetEdge?: string;
bartektartanus marked this conversation as resolved.
Show resolved Hide resolved
editedBy: string;
editedAt: string;
}
35 changes: 21 additions & 14 deletions designer/client/src/components/ComponentDragPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { css } from "@emotion/css";
import React, { forwardRef, useEffect, useMemo, useState } from "react";
import React, { forwardRef, ReactPortal, useEffect, useMemo, useState } from "react";
import { useDragDropManager, useDragLayer } from "react-dnd";
import { createPortal } from "react-dom";
import { useDebouncedValue } from "rooks";
import { NodeType } from "../types";
import { ComponentPreview } from "./ComponentPreview";
import { DndTypes } from "./toolbars/creator/Tool";
import { StickyNotePreview } from "./StickyNotePreview";
import { StickyNoteType } from "../types/stickyNote";

function useNotNull<T>(value: T) {
const [current, setCurrent] = useState(() => value);
Expand Down Expand Up @@ -53,17 +55,22 @@ export const ComponentDragPreview = forwardRef<HTMLDivElement, { scale: () => nu
return null;
}

return createPortal(
<div ref={forwardedRef} className={wrapperStyles} style={{ transform: `translate(${x}px, ${y}px)` }}>
<div
style={{
transformOrigin: "top left",
transform: `scale(${scale()})`,
}}
>
<ComponentPreview node={node} isActive={active} isOver={isOver} />
</div>
</div>,
document.body,
);
function createPortalForPreview(child: JSX.Element): ReactPortal {
return createPortal(
<div ref={forwardedRef} className={wrapperStyles} style={{ transform: `translate(${x}px, ${y}px)` }}>
<div
style={{
transformOrigin: "top left",
transform: `scale(${scale()})`,
}}
>
{child}
</div>
</div>,
document.body,
);
}

if (node?.type === StickyNoteType) return createPortalForPreview(<StickyNotePreview isActive={active} isOver={isOver} />);
return createPortalForPreview(<ComponentPreview node={node} isActive={active} isOver={isOver} />);
});
1 change: 1 addition & 0 deletions designer/client/src/components/ComponentPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export function ComponentPreview({ node, isActive, isOver }: { node: NodeType; i
}));

const colors = isOver ? nodeColorsHover : nodeColors;

return (
<div className={cx(colors, nodeStyles)}>
<div className={cx(imageStyles, imageColors)}>
Expand Down
62 changes: 62 additions & 0 deletions designer/client/src/components/StickyNotePreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { css, cx } from "@emotion/css";
import React from "react";
import { BORDER_RADIUS, CONTENT_PADDING, iconBackgroundSize, iconSize } from "./graph/EspNode/esp";
import { PreloadedIcon, stickyNoteIconSrc } from "./toolbars/creator/ComponentIcon";
import { alpha, useTheme } from "@mui/material";
import { getBorderColor, getStickyNoteBackgroundColor } from "../containers/theme/helpers";
import { STICKY_NOTE_CONSTRAINTS, STICKY_NOTE_DEFAULT_COLOR } from "./graph/EspNode/stickyNote";

const PREVIEW_SCALE = 0.9;
const ACTIVE_ROTATION = 2;
const INACTIVE_SCALE = 1.5;

export function StickyNotePreview({ isActive, isOver }: { isActive?: boolean; isOver?: boolean }): JSX.Element {
const theme = useTheme();
const scale = isOver ? 1 : PREVIEW_SCALE;
const rotation = isActive ? (isOver ? -ACTIVE_ROTATION : ACTIVE_ROTATION) : 0;
const finalScale = isActive ? 1 : INACTIVE_SCALE;

const nodeStyles = css({
position: "relative",
width: STICKY_NOTE_CONSTRAINTS.DEFAULT_WIDTH,
height: STICKY_NOTE_CONSTRAINTS.DEFAULT_HEIGHT,
borderRadius: BORDER_RADIUS,
boxSizing: "content-box",
display: "inline-flex",
filter: `drop-shadow(0 4px 8px ${alpha(theme.palette.common.black, 0.5)})`,
borderWidth: 0.5,
borderStyle: "solid",
transformOrigin: "80% 50%",
transform: `translate(-80%, -50%) scale(${scale}) rotate(${rotation}deg) scale(${finalScale})`,
opacity: isActive ? undefined : 0,
transition: "all .5s, opacity .3s",
willChange: "transform, opacity, border-color, background-color",
});

const colors = css({
opacity: 0.5,
borderColor: getBorderColor(theme),
backgroundColor: getStickyNoteBackgroundColor(theme, STICKY_NOTE_DEFAULT_COLOR).main,
});

const imageStyles = css({
padding: iconSize / 2 - CONTENT_PADDING / 2,
margin: CONTENT_PADDING / 2,
borderRadius: BORDER_RADIUS,
width: iconBackgroundSize / 2,
height: iconBackgroundSize / 2,
color: theme.palette.common.black,
"> svg": {
height: iconSize,
width: iconSize,
},
});

return (
<div className={cx(colors, nodeStyles)}>
<div className={cx(imageStyles, colors)}>
<PreloadedIcon src={stickyNoteIconSrc} />
</div>
</div>
);
philemone marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading