Skip to content
This repository has been archived by the owner on Feb 25, 2024. It is now read-only.

Commit

Permalink
Merge branch 'dev' into andarist/fix-app-remounting
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/App.tsx
#	src/AppHead.tsx
#	src/authMachine.ts
#	src/pages/[sourceFileId].tsx
#	src/pages/_app.tsx
#	src/sourceMachine.ts
#	tsconfig.json
  • Loading branch information
Andarist committed Sep 9, 2021
2 parents ac8d42c + ccd5337 commit cf0e5ca
Show file tree
Hide file tree
Showing 31 changed files with 1,039 additions and 384 deletions.
8 changes: 8 additions & 0 deletions .changeset/poor-moose-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'xstate-viz-app': minor
---

Embedded Mode!

The visualizer/inspector can now be used in the embedded mode. In this mode, some parts of the application can be configured such as control buttons, panning, zooming, etc.
The most important parameter to configure embedded mode it the `mode` that can be one of `viz`, `full` or `panels`.
5 changes: 5 additions & 0 deletions .changeset/quick-wasps-matter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"xstate-viz-app": patch
---

When you press visualize, machines will now automatically 'fit to view'. This prevents various bugs around state machines appearing not to be visible, when they're actually just off screen.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.next/*
196 changes: 196 additions & 0 deletions cypress/integration/embedded-mode.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import { GetSourceFileSsrQuery } from '../../src/graphql/GetSourceFileSSR.generated';

const SOURCE_ID = 'source-file-id';

const getSSRParam = (
data: Partial<GetSourceFileSsrQuery['getSourceFile']> & { id: string },
) => {
return encodeURIComponent(JSON.stringify({ data, id: data.id }));
};

describe('Embedded mode', () => {
describe('default (mode:viz)', () => {
before(() => {
cy.interceptGraphQL({
getSourceFile: {
id: SOURCE_ID,
text: `
import { createModel } from "xstate/lib/model";
import { createMachine } from "xstate";
createMachine({
id: "simple",
states: {
a: {},
b: {},
},
});
`,
},
});
cy.visit(
`/viz/embed/${SOURCE_ID}?ssr=${getSSRParam({
id: SOURCE_ID,
})}`,
);
});
it('panels should be hidden', () => {
cy.getPanelsView().should('be.hidden');
});
it('zoom and pan buttons group should be hidden', () => {
cy.getControlButtons().should('not.exist');
});
it('canvas header should be hidden', () => {
cy.getCanvasHeader().should('not.exist');
});
});

describe('mode:panels', () => {
beforeEach(() => {
cy.interceptGraphQL({
getSourceFile: {
id: SOURCE_ID,
text: `
import { createModel } from "xstate/lib/model";
import { createMachine } from "xstate";
createMachine({
id: "simple",
states: {
a: {},
b: {},
},
});
`,
},
});
});
it('should show panels view', () => {
cy.visit(
`/viz/embed/${SOURCE_ID}?ssr=${getSSRParam({
id: SOURCE_ID,
})}&mode=panels`,
);
cy.getPanelsView().should('be.visible');
});
it('should show CODE panel by default', () => {
cy.visit(
`/viz/embed/${SOURCE_ID}?ssr=${getSSRParam({
id: SOURCE_ID,
})}&mode=panels`,
);
cy.findByRole('tab', { name: /code/i }).should(
'have.attr',
'aria-selected',
'true',
);
cy.getMonacoEditor().should('be.visible');
});
it('code editor should be readonly', () => {
const editor = cy.getMonacoEditor();
editor.type('something');
editor.within(() =>
cy.findByText(/cannot edit in read-only editor/i).should('be.visible'),
);
});
it('an original link to the visualizer should be shown', () => {
cy.findByRole('link', { name: /open in stately\.ai\/viz/i })
.should('be.visible')
.should(($a) =>
$a
.attr('href')
?.includes('/viz/c75ccdc4-418d-4ade-9059-90f0fc4ddbd1'),
);
});
it('should be able to make code editor editable', () => {
cy.visit(
`/viz/embed/${SOURCE_ID}?ssr=${getSSRParam({
id: SOURCE_ID,
})}&mode=panels&readOnly=0`,
);
const editor = cy.getMonacoEditor();
editor.type('something');
editor.contains('something');
});
it('should be able to choose active panel', () => {
cy.visit(
`/viz/embed/${SOURCE_ID}?ssr=${getSSRParam({
id: SOURCE_ID,
})}&mode=panels&panel=state`,
);
cy.findByRole('tab', { name: /state/i }).should(
'have.attr',
'aria-selected',
'true',
);
cy.getStatePanel().should('be.visible');
});
it('should be able to hide the original link', () => {
cy.visit(
`/viz/embed/${SOURCE_ID}?ssr=${getSSRParam({
id: SOURCE_ID,
})}&mode=panels&showOriginalLink=0`,
);
cy.findByRole('link', { name: /open in stately\.ai\/viz/i }).should(
'not.exist',
);
});
it('the visualize button should be hidden', () => {
cy.visit(
`/viz/embed/${SOURCE_ID}?ssr=${getSSRParam({
id: SOURCE_ID,
})}&mode=panels`,
);
cy.contains('button', /visualize/i).should('not.exist');
});
it('the visualize button should be shown if readOnly is disabled', () => {
cy.visit(
`/viz/embed/${SOURCE_ID}?ssr=${getSSRParam({
id: SOURCE_ID,
})}&mode=panels&readOnly=0`,
);
cy.findByRole('button', { name: /visualize/i }).should('be.visible');
});
it('the "New" and "Login to fork" should be hidden', () => {
cy.visit(
`/viz/embed/${SOURCE_ID}?ssr=${getSSRParam({
id: SOURCE_ID,
})}&mode=panels`,
);
[/new/i, /login to fork/i].forEach((text) => {
cy.contains('button', text).should('not.exist');
});
});
});

describe('mode:full', () => {
before(() => {
cy.interceptGraphQL({
getSourceFile: {
id: SOURCE_ID,
text: `
import { createModel } from "xstate/lib/model";
import { createMachine } from "xstate";
createMachine({
id: "simple",
states: {
a: {},
b: {},
},
});
`,
},
});
cy.visit(
`/viz/embed/${SOURCE_ID}?ssr=${getSSRParam({
id: SOURCE_ID,
})}&mode=full`,
);
});
it('should show both canvas and panels', () => {
cy.getCanvasGraph().should('be.visible');
cy.getPanelsView().should('be.visible');
});
});
});
12 changes: 8 additions & 4 deletions cypress/integration/saving.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ describe('Saving', () => {
getLoggedInUser: {
id: 'id',
},
createSourceFile: sourceFileToBeCreated,
createSourceFile: {
id: 'source-file-id',
name: 'Source File',
owner: {
id: 'id',
},
},
});

cy.visit('/viz');
Expand All @@ -29,15 +35,13 @@ describe('Saving', () => {

cy.findByRole('button', { name: /Save/ }).click();

cy.findByLabelText(/Choose a name/i).type('My awesome source file');
cy.findByLabelText(/Choose a name/i).type('Source File');

cy.findByRole('button', { name: /Submit/ }).click();

cy.contains(/New file saved successfully/i);

cy.url().should('contain', 'source-file-id');

cy.contains('My awesome source file');
});

it('Should allow you to save an existing file', () => {
Expand Down
35 changes: 35 additions & 0 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,26 @@ const getMonacoEditor = () => {
return cy.get('.monaco-editor').first();
};

const getPanelsView = () => {
return cy.findByTestId('panels-view');
};

const getCanvasHeader = () => {
return cy.findByTestId('canvas-header');
};

const getStatePanel = () => {
return cy.findByTestId('state-panel');
};

const getCanvasGraph = () => {
return cy.findByTestId('canvas-graph');
};

const getControlButtons = () => {
return cy.findByTestId('controls');
};

type DeepPartial<T> = T extends Function
? T
: T extends Array<infer U>
Expand Down Expand Up @@ -188,6 +208,16 @@ declare global {
inspectMachine: typeof inspectMachine;

visitVizWithNextPageProps: typeof visitVizWithNextPageProps;

getPanelsView: typeof getPanelsView;

getCanvasHeader: typeof getCanvasHeader;

getStatePanel: typeof getStatePanel;

getCanvasGraph: typeof getCanvasGraph;

getControlButtons: typeof getControlButtons;
}
}
}
Expand All @@ -199,3 +229,8 @@ Cypress.Commands.add('interceptGraphQL', interceptGraphQL);
Cypress.Commands.add('visitInspector', visitInspector);
Cypress.Commands.add('inspectMachine', inspectMachine);
Cypress.Commands.add('visitVizWithNextPageProps', visitVizWithNextPageProps);
Cypress.Commands.add('getPanelsView', getPanelsView);
Cypress.Commands.add('getCanvasHeader', getCanvasHeader);
Cypress.Commands.add('getStatePanel', getStatePanel);
Cypress.Commands.add('getCanvasGraph', getCanvasGraph);
Cypress.Commands.add('getControlButtons', getControlButtons);
Loading

0 comments on commit cf0e5ca

Please sign in to comment.