Documentation
Libraries:
React – JavaScript library for making complex UI in a performant and maintainable way
Material UI – a collection of pretty pre-made UI components, designed according to well-researched UI standards
Cypress – an end-to-end testing library
Running Cypress tests will involve literally spinning up a browser and executing automated actions like clicking on buttons and asserting results after those actions
Jest – a unit testing library
When used with React Testing Library, it lets you check whether React components will have the HTML or state that you expect when an event takes place. Eg. after you perform user actions like button clicks, you might assert that a counter value goes up.
Various other libs – npm has a huge amount of great libraries. We’re using a number of them such as
react-console-emulator
,react-helmet
,react-cookie
, etc.It’s preferable to not re-invent the wheel, but we should be careful not to bloat our package.json with poorly-supported dependencies.
Directory Structure:
Below is a breakdown of the client
directory structure. This should be used as a reference for figuring out which part of the codebase is responsible for doing or displaying what.
For people working on the UI and design, you mainly need to be aware of what’s inside
client/components
andclient/views
.For people working on the visualiser UI specifically (the controller, terminal, etc.
For people working on the visualiser, you mainly need to be aware of what’s inside
visualiser-src
.For anyone trying to add a new visualiser and its commands into our webpage, see the section 'Adding a New Visualiser'.
You shouldn’t worry about any file you see in the project that isn’t included in the client
directory, eg. Dockerfiles, Jest config, build
folders, etc.
client/
package.json
src/
App.scss # Contains styles that we want globally applied, eg. scrollbar styles, default fonts, etc.
App.tsx # The root React UI component. This is where you should add new routes.
assets/ # A bunch of images, fonts and other static content that you wouldn't put next to your source code
components/ # All our React components (minus the pages themselves - those belong in the 'views' directory!)
Autocomplete # Basically a dropdown that suggests autocompletion results. See mui.com/components/autocomplete/.
Features # Some components used to help showcase our project features on the landing page.
FloatingWindow # The collapsible floating window on the visualiser, used for the operations menu and the code snippets
Footer # Footer element at the bottom of some pages, showing social links and links to other places.
Frame # Literally like picture 'frames', used to decorate showcase images
Gallery # A pretty container for pictures. Used to showcase a collection of items that have pictures. See mui.com/components/image-list/.
HorizontalRule # Basically prettier <hr /> components. See mui.com/components/dividers/.
Loader # Some fancy spinners, used to show some content is loading. See mui.com/components/progress/.
Navbars # Top navbar and side navbar
PageLayout # Contains some fundamental UI that is reused by our pages. Eg. the topnav, some container padding, etc.
Tags # A list of 'chip' components. It's mainly used to show tags for a topic like "COMP2521", "COMP1511", etc. See mui.com/components/chips/.
Topics # Contains the UI cards for all the topics (linked list, BST, etc.) that appear on the homepage.
Visualiser # Our visualiser UI. This includes the controller UI, terminal, GUI form, etc.
# This DOES NOT include any visualiser implementation! This is purely the surrounding UI. For the actual implementation, see 'visualiser-src'
constants/ # Contains global config and values that would otherwise be hard-coded in our source code.
api.ts # This contains important API connection info, eg. what the API URL is.
cookies.ts # This contains the names of cookies we're using. Eg. We need a name for the cookie that tracks whether the user should have dark mode on or off.
main.tsx # The REAL root UI component. This should rarely be touched unless we're doing fundamental changes. See 'App.tsx'.
structsThemes.ts # Our colour palette!
utils/ # A bunch of reusable functions.
apiRequests.ts # Contains all the functions you need to grab or change content from the API. Eg. there's functions for fetching lessons, topics, etc.
markdown-util.ts # Some functions for working with markdown.
Notification.ts # Spawns a small notification/popup. Great for non-invasive error messages and communicating short messages with the user.
url.ts # Some string manipulation functions, specifically for working with URL-like strings.
pages/ # Our pages. These are React components that specifically define page layout and content. For specific UI elements, see 'components'.
Feedback.tsx
HomePage.tsx # Our landing page.
Page404.tsx # A cool 404 page for when the user navigates to a route that we haven't defined. Eg. structs.sh/non-existent-page.
VisualiserPage.tsx
visualiser-src/ # Our visualiser source code. All the internal visualiser magic happens here. This does NOT define anything related to UI - for that, see 'components/Visualiser'.
binary-search-tree-visualiser/ # BST visualiser layout and operations. Includes things like insertion, rotation, etc. and drawing the tree.
controller # Executes controller operations like play, pause, speed change, etc. and manages state such as the animation timeline and operations history.
linked-list-visualiser # Linked list visualiser layout and operations. Includes things like insertion, deletion, etc. and drawing the linked list.
typedefs.ts # Custom data types used by the Visualiser implementation.
Styling with styled components
Using global CSS/SCSS is a nightmare in a large project because you will likely encounter name collisions and CSS specificity issues.
Using inline styles is limited (doesn’t allow for self-selectors like &
, pseudo-selectors like :hover
, media queries etc.)
To style a component, we can use Material UI styled components:
Suppose you’re working on
Menu.jsx
.At the top of the file, write a styled component
const Container = styled('div')(({ theme }) => ({
margin: 10px,
}))
Use the styled component in the JSX
const Menu = () => {
return <Container>...<Container />
}
Visualiser Docs:
This section provides a high-level explanation of the architecture behind the visualiser’s internal logic.
The visualiser logic and source code exists separately to the React codebase.
Diagram:
This diagram below was built with LucidChart. It shows a bird’s-eye view of the classes and files used to build the visualiser and controller.
Link to diagram with comment permission enabled: Structs.sh Visualiser Architecture : Lucidchart
To get edit permission, ask your team leads!
Adding a New Visualiser:
To add a new visualiser, you’ll need to need to make a new directory under visualiser-src
and create files that follow a similar architecture as above. Use the existing linked-list-visualiser
and binary-search-tree-visualiser
as references.
Note that you won’t need to create the AnimationController
and AnimationProducer
classes – they already exist.
To make it show up on the React client, you must
Add an entry for the visualiser into the ‘Topics’ section of the database
Add an entry for the visualiser in the DataStructure enum type under
visualiser-src/common/typedefs.ts
(make sure the name is consistent with the lowercase version of the database topic title)Add a case statement for your data structure under
visualiser-src/common/GraphicalDataStructureFactory.ts
to return your GraphicalDataStructure.
Adding a New Operation to a Visualiser:
Creating the logic of the animations
Code up the operation in the corresponding
Graphical[DataStructure]
in a new method. Add documentation for the arguments it accepts.Note: for now, arguments are non-negative integers between 0 and 999 only.
Write up a code snippet in a separate file (in C, as a multi-line string). This should look similar to what you wrote in step 1.
Identify the lines you want to animate and design the look of the animation
This is up to you (be as creative as you want!), but make sure it’s consistent with the rest of the visualiser, and make sure it is true to the steps of the operation.
Add a
[DataStructureOperation]AnimationProducer
class file under theanimation-producer
directory in the corresponding data structure directory invisualiser-src
Code up the SVG animations for each line you want to animate – one method corresponding to each code snippet line you identified in step 3.
Write a method that renders a code snippets you wrote in step 2. Make use of the
renderCode(code: string)
method.This involves calling
addSequenceAnimation
for animations that happen at the same time.TIP: Name AnimationProducer methods after the semantics of each line as opposed to its visuals. For example,
.appendNode()
as opposed to.showSVGAtLastPosition()
. This will guide you on what the animation looks like, and decrease your chances of creating animations that are misleading.TIP: Before writing an
AnimationProducer
method, have a look to see whether other operations already have it.
Instantiate the
[DataStructureOperation]AnimationProducer
class in the method you wrote in step 1 and render the code snippets.Call
doAnimation
(in the case that there is no corresponding code snippet line) ordoAnimationAndHighlight
(to highlight a code snippet line for the duration of your animation) on theAnimationProducer
method and its arguments.Return the
AnimationProducer
from the method.
If unsure, check out the linked list and binary search tree visualiser as reference.