yarn install
yarn dev
Open http://localhost:3000 with your browser to see the result.
You can start editing the page by modifying pages/index.tsx
. The page auto-updates as you edit the file.
API routes can be accessed on http://localhost:3000/api/hello. This endpoint can be edited in pages/api/hello.ts
.
The pages/api
directory is mapped to /api/*
. Files in this directory are treated as API routes instead of React pages.
The following variables are needed to connect to deployed DBs/APIs:
- OPENSEARCH_USER
- OPENSEARCH_PASSWORD
- OPENSEARCH_URL
- PLAYDUST_API_HOST
These should be added to an .env
file, values can be found in Vercel
NOTE: If the values of these variables are sent via Discord, a trailing /
may get appended to the OPENSEARCH_URL/PLAYDUST_API_HOST values. If the trailing slashes are not removed, the local deployment will be unable to properly query OpenSearch.
Enviroment variables are needed to connect to our API, follow these steps for correct configuration
- Clone API project here
- Run API project with another port (PORT=8000 npm run dev)
- Create a .env.local file in the root folder
- Add API_HOST key with API server URL as value
All those steps are for connection in local to our API.
- the top level entity
- playdust-ui utilizes a single next.js page, that utilizes hash routing to move between tabs
- the playdust app contains 1 to many tabs
- Tabs represents a list of windows
- Allows user to quickly flip between these windows and are persisted between sessions
- A playdust tab has 1 to many windows
- Every window has one search input / address bar which defines what it renders.
- windows pick the right page to render the content referenced by their search-input/address bar value.
- 1 to many pages, within a parent tab can be viewed at once
- Each page takes a search-input/address as it's source of truth.
- A single page is rendering the results of it's search in isolation.
- A page is responsible for bootstrapping itself, and composing its needed modules
- Each page shares the same interface for props
- page states are stored as strings, and each window is responsible for parsing its state
export interface Window {
state: string;
type: WindowUnion;
}
export interface Tab {
id: string;
windows: Window[];
}
export interface App {
tabs: Tab[];
selectedTabId: string;
}
Analytics are recorded via Google Analytics version 4. For Google Analytics setup, reach out to a user with Google Analytics admin access (stan@playdust.com, eugene@playdust.com, philipp@playdust.com, operations@playdust.com). Configuration of the Google Analytics version 4 measurement id is done through the .env file under the key GOOGLE_ANALYTICS_4_MEASUREMENT_ID and it's current value should be set to G-96LZMYF7WY.
The guiding principle for the file/code structure is to be as self documenting as possible to avoid manual maintenance.
src/
├─ App/
├─ api/
├─ _types/
The api folder contains all nextJS api endpoints in a folder structure following the data / URL structure.
types/
contains all shared type definitions between the api/
and `App/``
MyComponent/
├─ _atoms/
│ ├─ myAtom.ts
│ ├─ mySelector.ts
├─ _hooks/
│ ├─ useMyHook.ts
├─ _helpers/
│ ├─ myHelper.ts
├─ _types/
│ ├─ myType.ts
├─ _sharedComponents/
│ ├─ MySharedComponent/
│ │ ├─ MySharedComponent.tsx
│ │ ├─ [...]
│ ├─ MyCommonGrid.tsx
├─ MyComponent.tsx
├─ MyComponentTitle.tsx
├─ MyComponentFooter.tsx
├─ MyComponentContent/
│ ├─ MyComponentContent.tsx
│ ├─ [...]
-
If a component becomes too complicated for a single file, a folder
ComponentName/
is created containingComponentName.tsx
and all files related to the component.Every component (file) in the folder, must either be the actual parent component or a direct child of the parent.
If reasonable, sub components are named
ComponentName[SubComponent].tsx
. This can be broken or abbrevated in order to prevent component names from getting too long. -
Selectors are an implementation detail of atoms
Atoms are split out into the closest
_atoms/
in the component tree. Atom files should end withAtom.ts
._atoms/
should contain a flat list of files.Reused Atoms are moved to the closes shared parent components
_atoms/
folder. -
All external and/or shared logic between components is using the hooks interface.
Hooks are split out into the closest
_hooks/
folder within the component tree and files are named after the hook they exportuseMyHook.ts
_hooks/
should contain a flat list of files.Reused Hooks are moved to the closest shared parent components
_hooks/
folder. -
Types should be defined as close to data creation as possible. Types should either end in
Type.ts
orProps.ts
.Whenever possible, types should be defined and verified alongside Atoms and Selectors.
_types/
should contain a flat list of files.Reused types are moved to the closest shared parent components
_types/
-
Code that is reused between
_atoms
and/or_hooks
goes into the_helpers/
folder._helpers_/
should contain a flat list of files.Reused helpers are moved to the closest shared parent components
_helpers/
-
Usually all components are defined within their parent components folder.
_sharedComponents/
should contain a flat list of Components (files or folders).Reused Components are moved to the closest shared parent components
_sharedComponents/
folder.
-
Functions that create
hooks
oratoms
are prefixed withmake
(iemakeUseMyHook.tsx
ormakeMyAtom.ts
) and live in_hooks
or_atoms
respectively. -
To keep the file structure self documenting, each file must only export a single
default
export which it is named after. iE.MyComponent.tsx
exports<MyComponent … />
A file can define multiple components/atoms/selectors for internal use, but can only export the one it is named after.
-
The real value of Typescript comes when there are no shortcuts being taken. ¨ Every shortcut introduces a potential for unexpected failures.
-
When the above folder structure is followed, imports adhere to the following easily visible rules:
- No
import *
since all files export onlydefault
. - Traversing up (ie
../../../_atoms
)- should always lead to a
_[folder]
- should never lead to a Component
- should always lead to a
- Traversing down Components (ie
./MyComponent/MyComponentTitle
)- should always lead to a Component
- should never lead to a
_[folder]
- Traversing into
_[folders]
(ie./_atoms/myAtom.tsx
) should not exceed 2 levels of depth.- Correct:
./_atoms/myAtom.tsx
- Correct:
./_sharedComponents/MyComponent/MyComponent
- Incorrect:
./_sharedComponents/MyComponent/MyComponentTitle
In this case,
MyComponentTitle
should be moved up to_sharedComponents/
- Correct:
- No
-
Writing to atoms should be triggered by events and not by the render loop.
Using
setAtomValue
withinuseEffect
creates an additional, (usually) avoidable, render loop.In (almost) all cases the logic in
useEffect
can be moved to aselector
to avoid the problem.There can be exceptions to this rule very high up in the app to set default environment atoms (if they can't be passed into
RecoilRoot
for some reason). These should be isolated and commented to explain their necessity. -
- we are using
emotion
as our CSS implementation- the preferred method is to use the
emotion/styled
API to add styles to components
- the preferred method is to use the
- VSCode Syntax Highlighting for styled components
- we are using
In order for a build to succeed the following type/lint checks must pass:
- typescript
- eslint
- prettier
- coding guidelines
The following scripts can can be used to view errors:
# typescript
yarn tsc:check
# eslint
yarn lint
# prettier
yarn prettier:check
# coding guidelines
yarn guidelines:check
The following scripts can attempt to automatically fix errors:
# eslint
yarn lint:fix
# prettier
yarn prettier:write
# coding guidelines
yarn guidelines:write
Also recommended is to use the git commit hook, which will automatically check/fix your staged files before committing. This can be turned on/off with the following scripts:
# enable git hook
yarn hooks:install
# disable git hook
yarn hooks:uninstall