Skip to content

Commit

Permalink
Patch UI (#56)
Browse files Browse the repository at this point in the history
* Add Navbars and Tabs for Patch

Also includes first version of support for hotkeys
to navigate.

* Add Navbars and Tabs for Patch

Also includes first version of support for hotkeys
to navigate.

* Add ag-grid example table to Patch

* Add "Add New Fixtures" Button

* Add example data

Just noticed ag-grid won't support range
selection in community edition. Need to use
different table library, e. g. react-data-grid,
tabulator or react-table with our own frontend.

* React Table Example

* Tabulator Example

* Add Example Data and fix Table Height

* Implement PatchState as Context with UUIDs

* Rudimentary Fixture Type Page

* Add New Fixture Route and complicate Routing

* Rudimentary "Add Fixture" Page Layout

* Add WS connect

Reconnecting WebSocket will try to connect.
If it can't, a blocking pop-up will be displayed.

WS uses Proxy in Development, so that on can use the CRA
development server to handle website request while
cueglow-server handles all WebSocket connections.

* Update dependencies and fix some warnings

* Add Cypress

Includes setup according to docs and removing some warnings

* Add custom npm commands for Cypress

For development, start the dev server with `npm start`, then start the
Cypress Test Runner with `npm run cy`. To connect the WebSocket,
start the cueglow-server from its directory with `gradlew run`. Cypress
will direct its request to locahost:3000.

To test against the production server with Cypress, start cueglow-server
with `gradlew run` and start Cypress with `npm run e2e`. It will then direct
all request to localhost:7000.

* Add Cypress Navigation Test and disable examples

* Set up simple smoke test in Jest

Also refactored index.tsx into App.tsx and index.tsx. Jest renders the
App with some fiddling (not transforming blueprintjs and mocking
out sass variable loader). However, for testing the frontend Cypress
should be preferred. Jest would be useful for simple unit tests that
do not depend on much else in the App, like testing an algorithm.

* Make outline for WS API Mock Test

* Remove Non-API WebSocket Message on Connect

* Update Dependencies

* Remove sass-extract-loader for Blueprint Variables

sass-extract-loader is practically unmaintained, the last commit was
about three years ago. I replaced it with manually added SCSS module
exports that are wrapped in a TypeScript module.

* Update to npm v7

* Replace react-hotkeys-hook with Blueprint

* Remove reconnecting-websocket

Instead, we'll try once to connectr the WebSocket on page load.
See #52 for
implementing automatic reconnection

* Only Render App When Connection is Open

if connection isn't open, we render other pages.

* Refactor App into Multiple Files

* Delete index.css and move to SCSS

* Resolve Lint Warnings

* Clean Up ConnectionProvider Architecture

Specifically, use a Property Event Handler in a connecitonProvider class.
The interface hook was moved to the single component where
it is allowed to be used. The hook registers its own event handler.

* Write Some Types for Patch Messages

* Add Gradle Task without Frontend Build

closes #54

* Subscribe to Patch Topic

Current design is clunky but will be improved down
the line.

* Implement Upload Fixture Type

* Handle Add Fixture Types

* Avoid Setting Patch Callback on Every Change

* Make Patch Data Handler Global Singleton

This way, the message handler can call into the
patch data handler, making it superfluous to register
callbacks.

* Create Classes for Outgoing Messages

* Implement Crude Remove Fixture Types

The UI, the code, everything is suboptimal here.
Significant issues were marked with TODO comments.

* Implement addFixtures and removeFixtures

* Test Add/Remove Fixtures/Fixture Types

Still need to fix warning about Cypress commands in async function.

* Refactor Setup from JS to Cypress Promise

* Refactor Cypress Tests

- Change all files to TypeScript
- Refactor clearFixtureTypes to custom command
- Add dataCy command example from website

* Warn on Explicit any type

* Show Message When No Fixture Type Is Selected

* Change Title and favicon

The green play button isn't final, but better than the React logo.

* Test WebSocket Connection Error Behavior

* Remove Explicit Any

There seems to be issues with typings for react-tabulator not being
recognized.

* Remove TODOs: Refactor PatchDataProvider

* Move Tabulator CSS Imports into index.scss

* Remove TODOs in NewFixture

- missing default handling moved to #50
- current implementation with Blueprint's Suggest works fine
- Triangle is not super needed, so let's drop it

* Wrap ReactTabulator in Strongly Typed Element

We achieve this by using types from @types/tabulator-tables and
applying them for the React element from
react-tabulator.

This has the benefit of much improved IDE tooling and better
type safety.

* Fix: Show Selection Change in FixturePatch

Previously, one had to click twice on a fixture row to
show the selection, although it was selected for remove after the
first click. Fixed with useMemo, though I don't know why it works.

* Allow Selection of Multiple PatchFixtures

Also change selection callbacks from rowSelected to rowSelectionChanged
to include deselection.

* Fix Gradle Deprecation Warnings

* Fix RunNoNpm

Previously, RunNoNpm would exclude the relevant task during the
configuration phase, meaning the excluded task would never run at all.
This is now fixed: npm_run_build runs when using gradle run, but not
with gradle runNoNpm.

* Update Gradle from 6.7 to 7.1

This allows us to use Java 16 (not possible with 6.x due to
gradle/gradle#15538).

* Fix Cypress Jank

* Add Hotkey to Remove Buttons

* Make FID Editable with Validation

Edits aren't sent to the server yet.

* Make Universe and Address in Patch Editable

* Synchronize Patch Fixture Edits to Server

I introduced lodash as depedency. I don't like it, but it's a common
tool and writing the object merge in TypeScript is very annoying.

Open Issue: Empty Values for the numbers either do nothing
or crash the server connection.

* Fix Connection Drop When Entering Empty FID Value

The protocol does not have a reserved missing value for FID, therefore
an empty FID is just not allowed. This is now enforced by validation.

* Refactor Error Message From Tabulator Validators

* Allow Empty DMX Address

## What API allows:
- set dmxAddress to -1 to unpatch
- set universe to -1 to unpatch

## What we do:
- if  universe is -1, we show an empty string with a formatter, but
this can't be set by the user due to
olifolkerd/tabulator#1700
- dmxAddress can be set to empty string which will be translated with a mutator
to -1 for the server and the other way around

* Add Tests for Patch Fixture Editing

* Add Navigation and Add Hotkeys

* Refactor into HotkeyHint Component

* Fix Typo in Patch Tab "FixtureTypes"

added Space between words

* Organize PatchWindow Imports

* Fix Unique Key Warning on HotkeyHint

* Fix Unique Key Warning on HotkeyHint with Chords

Doesn't occur in our application yet, so I missed it in the
previous commit.

* Keyboard-Only Story for Add Fixtures

- proper tab order
- start focus in first field
- advance to next field after hitting enter in suggest fields
- submit on ctrl+enter
- Test for the above

* Fix Crash: Add Fixtures without Selecting GDTF

now form will not submit when no fixture type is selected

* Reset Selected DmxMode when Fixture Type changes

* Declare Min/Max Values on All New Fixture Fields

* Accept -1 for Universe and Address Server-Side

* Half-Working Form Validation with react-hook-form

* Allow Exiting out of NewFixtures with Esc

Normal HTML inputs don't blur on Esc, so it should be okay
to exit the page on Esc as announced by the Hotkey Label.

Possible Issue: A user assumes Esc will exit out of an input and
the user loses the data her already entered.
Since not much data can be entered in this form,
I think it is fine.

* Show Errors for Fixture Type in AddFixtures

Also delete TODO for Enter on Numeric Fields to
advance to the next field - this is surpsrisingly hard because
JS cannot hook into the native tabIndex.

* Simplify onSubmit in AddFixtures

* Add Error Tooltip for DmxMode in AddFixtures

* Replace Ref for focus by react-hook-form

* Move from useState to watch in reacht-hook-form

* A Little Testing on Form Validation

* Refactor NavbarExitWithTitle into Single Component

* Refactor into ValidatedSuggest

* Refactor NewFixture into Multiple Files

* Install styled-components

For example, it is used in two places, showing off how to
use it inline with the css prop and with the styled interface.

When using the css prop, an import must be manually added:
`import { } from 'styled-components/macro';`

* Refactors to Improve Readability

* Refactor Blueprint Variables API

* Clean Up A Bit in Cypress Folder

* Update CONTRIBUTING with Frontend Info

* Reduce Windows-Linux Redundancy in CONTRIBUTING

* Update to Cypress 8.3.1

* Fix Failing Tests in Chromium

This is a strange issue - clicking on the label and typing does work in
Firefox but not in Chromium. But the issue only shows up when
something is already typed into the field - it works for empty inputs.

Now we just directly type into the input associated with the label,
which works. The code for the utility function to do this is taken from
https://glebbahmutov.com/cypress-examples/6.5.0/recipes/form-input-by-label.html#reusable-function

* Remove Force-Click in Cypress Test

Don't know why it works without force now but I'm happy it does.

* Add Confirmation Dialog when Removing GDTF

* Fix Bug when Re-Adding GDTF

Steps to reproduce fixed bug:
(do not reload between steps)
- Add GDTF file
- Remove GDTF
- Try to add it again

Oberseved before fix: Nothing happens when trying to add again - have to
reload to make it work.

Expected and observed after fix: GDTF is added again the second time

* Move TODO to Issue #57

* Fix Remove GDTF Hotkey Label
  • Loading branch information
Firionus committed Oct 19, 2021
1 parent 40c7329 commit cb3ff46
Show file tree
Hide file tree
Showing 64 changed files with 34,257 additions and 4,436 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -108,3 +108,6 @@ fabric.properties
.idea/codestream.xml

# End of https://www.toptal.com/developers/gitignore/api/intellij

# automatically generated with react-scripts 4.0.1
cueglow-webui/.eslintcache
165 changes: 111 additions & 54 deletions CONTRIBUTING.md
Expand Up @@ -9,121 +9,173 @@ You can help to improve CueGlow in many different ways:
- UX and Design Work
- Telling Other People about CueGlow

If you are not sure how you can contribute, don't hesitate to [open
an issue](https://github.com/cueglow/cueglow/issues/new). We'd love to hear from
If you are not sure how you can contribute, don't hesitate to [open an
issue](https://github.com/cueglow/cueglow/issues/new). We'd love to hear from
you!

The following sections will help you to build CueGlow and to contribute
code.
The following sections will help you to build CueGlow and to contribute code.

## How to Build

### Linux/macOS

Make sure you have the following dependencies installed:

- git
- [JDK 15 with Hotspot](https://adoptopenjdk.net/installation.html?variant=openjdk15&jvmVariant=hotspot)
- [Node.js 14 LTS](https://nodejs.org/) (recommended to install via [NodeSource](https://github.com/nodesource/distributions) or [nvm](https://github.com/nvm-sh/nvm))
- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
- [JDK 16 with Hotspot](https://adoptium.net/installation.html?variant=openjdk16&jvmVariant=hotspot)
- [Node.js 14 LTS](https://nodejs.org/) (we recommend a version manager like [nvm](https://github.com/nvm-sh/nvm) for Linux/macOS or [nvm-windows](https://github.com/coreybutler/nvm-windows) for Windows)

First clone the CueGlow repository by running
```
git clone https://github.com/cueglow/cueglow.git
```
Go into the server folder with
```
cd cueglow/cueglow-server
```
Build and run CueGlow with
```
./gradlew run
```
then, go to [localhost:7000](http://localhost:7000) in the browser to see the UI.

If you run into any problems, feel free to [open an issue](https://github.com/cueglow/cueglow/issues/new).

On **Windows**, build and run the server with

### Windows

Make sure you have the following dependencies installed:

- [git](https://gitforwindows.org/)
- [JDK 15 with Hotspot](https://adoptopenjdk.net/installation.html?variant=openjdk15&jvmVariant=hotspot)
- [Node.js 14 LTS](https://nodejs.org/)

First clone the CueGlow repository by running
```
git clone https://github.com/cueglow/cueglow.git
```
Go into the server folder with
```
cd cueglow\cueglow-server
gradlew run
```
Build and run CueGlow with

For the rest of this file, all commands will be given for Linux/macOS. If you
are on Windows, adapt accordingly: Run `gradlew` instead of `./gradlew`, replace
forward with backward slashes, etc.

On **Linux/macOS**, build and run the server with

```
gradlew run
cd cueglow/cueglow-server
./gradlew run
```
then, go to [localhost:7000](http://localhost:7000) in the browser to see the UI.

If you run into any problems, feel free to [open an issue](https://github.com/cueglow/cueglow/issues/new).
Then, open [localhost:7000](http://localhost:7000) in the browser to see the
UI. If you run into any problems, have a look at our [known issues](#known-issues).

For the rest of this file, all commands will be given for Linux/macOS. If you are on Windows, adapt accordingly: Run `gradlew` instead of `./gradlew`, replace forward with backward slashes, etc.

## Repository Structure

The repository contains two main projects: The folder `cueglow-server` is a Gradle Kotlin project, the folder `cueglow-webui` is an npm project based on create-react-app with Typescript. When building the backend server with `./gradlew run`, Gradle calls `npm run build` to produce a production build of the website and bundles it with the generated jar file before starting the compiled jar.
The repository contains two main projects: The folder `cueglow-server` is a
Gradle Kotlin project, the folder `cueglow-webui` is an npm project based on
create-react-app with Typescript. When building the backend server with
`./gradlew run`, Gradle calls `npm run build` to produce a production build of
the website and bundles it with the generated jar file before starting the
compiled jar.

## Getting Started with Frontend Development

Starting from the root directory of the repository, build and run the backend server with
Starting from the root directory of the repository, build and run the backend
server without building the frontend by executing
```
cd cueglow-server
./gradlew run
./gradlew runNoNpm
```

To benefit from faster frontend compile times, automatic reload when changing files and other niceties, start the frontend deveopment server with
Then start the frontend development server with
```
cd cueglow-webui
npm start
```
The Web-UI from the development server should start in the browser at `localhost:3000`. The development server will serve http requests on port 3000 but forward all WebSocket connections to the backend server at `locahost:7000`.

To learn how to setup your editor, refer to the docs of [create-react-app](https://create-react-app.dev/docs/setting-up-your-editor).
The Web-UI from the development server should start in the browser at
`localhost:3000`. The development server will serve http requests on port 3000
but forward relevant connections to the backend server at `locahost:7000`. This
is configured in [setupProxy.js](cueglow-webui/src/setupProxy.js).

To learn how to setup your editor, refer to the docs of
[create-react-app](https://create-react-app.dev/docs/setting-up-your-editor) and
[styled-components](https://styled-components.com/docs/tooling#syntax-highlighting).

### Frontend Testing

We use Cypress for frontend testing. To test the site served by the frontend development server, use our custom npm script `cy`:
### Automated Frontend Testing

We use Cypress for frontend testing. To test the site served by the frontend
development server, use our custom npm script `cy`:
```
cd cueglow-webui
npm run cy
```
which should start the Cypress test runner. To learn how to use Cypress, please see the [Cypress Docs](https://docs.cypress.io/guides/overview/why-cypress.html).
which should start the Cypress test runner. To learn how to use Cypress, please
see the [Cypress
Docs](https://docs.cypress.io/guides/overview/why-cypress.html).

If you want to test the production site bundled with the backend server at `localhost:7000`, you can use the custom npm script `e2e` (for End-to-End testing):
If you want to test the production site bundled with the backend server at
`localhost:7000`, you can use the custom npm script `e2e` (for End-to-End
testing):
```
cd cueglow-webui
npm run e2e
```

For simple, non-GUI unit tests we use Jest which you can run with
```
npm test
```
[Testing Library](https://testing-library.com/docs/) is installed, but we
currently don't use Jest for UI tests and use Cypress instead.

### Frontend Code Structure

The Cueglow WebUI is a create-react-app based npm project utilizing TypeScript.
It builds on components from the [Blueprint.js](https://blueprintjs.com/docs/)
library.

Other notable tools are:
- Client-Side Routing: `@reach/router`
- Utilities: `lodash`
- CSS-in-JS: `styled-components`
- `styled` interface: `import styled from 'styled-components/macro'`
- `css` prop interface: put an empty import into the file for it to work:
`import { } from 'styled-components/macro';`
- Where possible, use SCSS Variables from Blueprint.js which are exported to
JavaScript in [BlueprintVariables](cueglow-webui/src/BlueprintVariables/BlueprintVariables.ts)
- Tables:
- `tabulator-tables`/`react-tabulator` is currently used for the Patch, but
has typing issues (I had to [re-wrap
it](cueglow-webui/src/Components/GlowTabulator.tsx)) and does not support
cell-level selection
- Future components should use `react-table` with `react-table-plugins` and
a custom frontend based on the Blueprint.js Table (which can't be used due
to [Firefox Scrolling Bug](https://github.com/palantir/blueprint/issues/1712))
- Form Validation: `react-hook-form` with `zod`

### Frontend Connection with Server

The WebSocket connection between frontend and server is managed outside of React
in the
[ConnectionProvider](cueglow-webui/src/ConnectionProvider/ConnectionProvider.tsx)
and associated modules. The current architecture works like this:
The singleton
`ConnectionProvider` ensures that a connection is established. Each connection
has an associated `SubscriptionProvider` and `MessageHandler`. The job of the
`SubscriptionProvider` is to subscribe/unsubscribe from the right topics. The
`MessageHandler` handles and dispatches incoming messages, e.g. to the
`patchDataHandler`. The `patchDataHandler` is a global singleton that tries to
keep a consistent representation of the data in the Patch topic at all times.
This state is fed to the React application via the Context API in the
`PatchDataProvider` component. It registers an update handler in the
`onPatchChange` field of the `patchDataHandler`. The `PatchDataProvider`
therefore must only be instantiated once.


## Branching Guide

We try to adhere to OneFlow Branching as described in https://www.endoflineblog.com/oneflow-a-git-branching-model-and-workflow. To create a new feature, branch off from main with
We try to adhere to OneFlow Branching as described in
https://www.endoflineblog.com/oneflow-a-git-branching-model-and-workflow. To
create a new feature, branch off from main with

```
git checkout -b feature/my-feature main
```

Once you want a review of your code or want to discuss some open questions, open
Once you want a review of your code or want to discuss some open questions, open
a Pull Request on GitHub.

## Code Style

All code should be well tested to ensure our goal of reliability.

Server or API features should be tested server-side in JUnit 5.
The client is then tested against the server during end-to-end testing in Cypress.
Server or API features should be tested server-side in JUnit 5. The client is
then tested against the server during end-to-end testing in Cypress.

## Possible Issues
## Known Issues

### npmInstall failed

Expand All @@ -137,4 +189,9 @@ FAILURE: Build failed with an exception.
Execution failed for task ':npmInstall'.
> A problem occurred starting process 'command 'npm''
```
you can try running `./gradlew --stop` to stop the Gradle daemon and then re-run `./gradlew run`.
you can try running `./gradlew --stop` to stop the Gradle daemon and then re-run
`./gradlew run`.

### Still didn't work?

If you have any remaining issues, please [open an issue](https://github.com/cueglow/cueglow/issues/new).
1 change: 1 addition & 0 deletions cueglow-server/build.gradle.kts
Expand Up @@ -15,6 +15,7 @@ plugins {
kotlin("jvm") version "1.4.10"
application
id("com.github.node-gradle.node") version "2.2.4"
id("com.dorongold.task-tree") version "1.5"
}

group = "org.cueglow"
Expand Down
Binary file modified cueglow-server/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion cueglow-server/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
2 changes: 1 addition & 1 deletion cueglow-server/gradlew
Expand Up @@ -130,7 +130,7 @@ fi
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`

JAVACMD=`cygpath --unix "$JAVACMD"`

# We build the pattern for arguments to be converted via cygpath
Expand Down

0 comments on commit cb3ff46

Please sign in to comment.