The Platform UI is the official Topcoder web app to host all modern user interfaces to be used by all users.
All future user interfaces at Topcoder will be implemented here. Pre-existing user interfaces will be ported to here over time until this is the only user interface any user sees when interacting with Topcoder.
NOTE: The information in this file describes our coding standards and best practices. All new code should follow these guidelines both when coding new features as well as porting old features. Please take the time to read through this file in detail.
The app uses CircleCI for CI/CD.
The dev
branch is auto-deployed to the dev environment: https://platform-mvp.topcoder-dev.com.
The master
branch is auto-deployed to the production environment: https://platform-ui.topcoder.com.
If a Jira ticket requires any code changes, it should have its own pull request.
PRs should be named as follows:
[TICKET-###] [Short Description] -> [target-branch-name]
e.g. GAME-174 Upload Badge Image Fix -> dev
PRs should also have a description that includes a link to the Jira ticket and a summary of what the PR is changing.
All branches use dev
as their source. All merges to dev
should be made via pull request and should be approved by application owner(s).
When working on Jira tickets, a branch should correspond with a single ticket.
When using subtasks, each parent ticket should have its own branch off dev
, and all subtasks branches should be merged into the parent ticket branch instead of directly to dev
.
Use the following naming convention for branches in order to link associated Git PRs and branches to the tickets:
[TICKET-###]_[short-description]
e.g.: PROD-1516_work-issue
We use Smart Commits to link comments and time tracking to tickets. You would enter the following as your commit message:
[TICKET #] #comment <commit message> #time <jira-formatted time>
e.g.: PROD-001 #comment adding readme notes #time 45m
- Node
- Yarn
- Typescript
- React Scripts
This app uses React 18, Typescript 4, and Node 22.
Use the VS Code IDE for MFE development.
Use the node version manager nvm to easily and safely manage the required version of NodeJS (aka, node). Download and install it per the instructions for your development operating system. Installing a version of node via nvm
will also install npm
.
NOTE: If the nvm command is not working it might be because the installation failed to update your paths variable properly. To try and fix this try installing nvm again using sudo.
Once nvm is installed, run:
% nvm install
At the root of the project directory you'll notice a file called .nvmrc
which specifies the node version used by the project. The command nvm use
will use the version specified in the file if no version is supplied on the command line.
See the nvm Github README for more information on setting this up.
NOTE: The current node version mentioned in the
.nvmrc
is22.13.0
You can verify the versions of nvm
, node
, and npm
using the commands below.
Command | Supported Version |
---|---|
% npm -v |
8.5.5 |
% node -v |
v22.13.0 |
% nvm --version |
0.39.1 |
% nvm current |
v15.15.0 |
NOTE: The
yarn start
command requires theNVM_DIR
env variable is set.
export NVM_DIR=~/.nvm
If you don't have this set globally, you can create your own personal config to define your local nvm dir.
You will need to add the following line to your hosts file. The hosts file is normally located at /etc/hosts
(Mac). Do not overwrite the existing localhost entry also pointing to 127.0.0.1.
127.0.0.1 local.topcoder-dev.com
- Open a terminal
- Run the following commands
% git clone https://github.com/topcoder-platform/platform-ui.git
% cd platform-ui
% yarn install
% yarn start
NOTE: The site must run on port 443 in order for auth0 to work and for the site to load properly. Mac users will need to run the app with elevated permissions, as in:
% sudo yarn start
Run following command to allow node to run apps on ports lowert than 500:
sudo setcap 'cap_net_bind_service=+ep' `which node`
SSL is required for authentication to work properly.
The yarn start
command serves the site using the cert and key in the /ssl directory, which authorize the https://local.topcoder-dev.com
URL.
By overriding the app to use port 443, you can use the authorized URL and trust the root CA to avoid SSL errors in the browser.
NOTE: Mac users will require running the app with elevated permissions in order to use a port lower than 500.
Easy way to overcome elevated permissions is to make use of:
sudo setcap 'cap_net_bind_service=+ep' `which node`
For easier development, it is recommended that you add this certificate to your trusted root authorities and as a trused cert in your browser. Google your browser and OS for more info on how to trust cert authorities.
Otherwise, you will need to override the exception each time you load the site. Firefox users may need to user an incognito browser in order to override the exception.
Each Application can have its own setup requirements. Please see each apps's README for further information.
Command | Description |
---|---|
yarn start |
Serve dev mode build with the default config |
yarn build |
Build dev mode build with the default config and outputs static files in /build |
yarn build:prod |
Build prod mode build with the prod config and outputs static files in /build |
yarn demo |
Serves the built files (by running yarn:build) for local testing |
yarn lint |
Run eslint against js/x and ts/x files and outputs report |
yarn lint:fix |
Run eslint against js/x and ts/x files, fixes auto-fixable issues, and outputs report |
yarn test |
Run unit tests, watching for changes and re-running per your specifications |
yarn test:no-watch |
Run unit tests once, without watching for changes or re-running |
yarn cy:run |
Run e2e tests once in local command with the site is running |
yarn cy:ci |
Run e2e tests once by circle ci |
yarn report:coverage |
Generate e2e coverage report in html format |
yarn report:coverage:text |
Generate e2e coverage report in text format |
Under "src/apps/platform" is to be found the "mainframe" of the platform application. This application only loads and serves all the other applications, and serves as the main router of the whole platform UI. It also renders the Universal Navigation's header & footer.
The goal for the PlatformUI is to eventually host as many apps from the Topcoder environment as possible. To accomodate this, each individual app has it's own "workspace" that is to be found under "src/apps". They can share components and common utilities by using libraries created under "src/libs", eg. "src/libs/ui".
The global (common) configuration files of the applications are to be found under "src/config/environments".
Note that we have some aliases defined in craco.confg.js
and tsconfig.paths.json
. These are defined for easier imports:
~
refers to 'src', so imports can be much cleaner:import { Button } from '~/libs/ui'
- in scss, you can point to the global ui styles, mixins & variables by using
@import '@libs/ui/styles/includes';
- you can define a new allias for a new app, eg. the earn app has it's own alias, and it can be used as
@import '@earn/styles/variables';
At the moment, a few applications are imported from different codebases as they are, only a few updates have been made to them, hence they are written in javascript rather than typescript. The goal is to have all applications transitioned to typescript eventuall. So, if you write any new component/any new application, please use typescript, as we'll eventually deprecate the JS code.
This is where all the applications under platform-ui will be created and live. Each application can have it's own configuration & setup.
Global (common) configurations shared between all apps under platform-ui
Import with ~/config
;
Shared code that should be stable and should not be modified unless expressly intending to modify the entire Platform UI.
As obvious as it may sound, but within the libraries themselves, we should not, import anything from the apps fodlers. The libraries should be standalone, at most they should rely on other libraries (eg. libs/core will import from libs/ui pages related to Auth).
See the Styling section for more details about stylesheets
NOTE: Apps should not import modules from anywhere other than libs. If it is necessary to import from outside the libs, the shared code should generally be moved to a lib under libs.
All of the routes for a given app (including root, section, and subsection routes) should be defined in a top-level file in it's own app folder.
i.e. [appName]Routes in /src/apps/[app-name]/src/[app-name].routes.ts
e.g. learnRoutes in /src/apps/learn/src/learn.routes.tsx
e.g. selfServiceRoutes in src/apps/self-service/src/self-service.routes.tsx
These routes then need to be imported in the Plaform App's platformRoutes:
import { learnRoutes } from '~/apps/learn'
import { selfServiceRoutes } from '~/apps/self-service'
export const appRoutes: Array<PlatformRoute> = [
...selfServiceRoutes,
...learnRoutes,
]
When loading a route component, please use the lazyLoad()
method defined in ~/libs/core
.
param | description |
---|---|
moduleImport: () => Promise<any> |
Function which imports the desired module |
namedExport?: string |
The name of the exported module (if the module has named exports) |
Eg:
// Lazy load the WelcomePage component
const WelcomePage: LazyLoadedComponent = lazyLoad(() => import('./welcome'), 'WelcomePage')
...
// Use the component as route element
export const learnRoutes: Array<PlatformRoute> = [
{
children: [
{
children: [],
element: <WelcomePage />,
...
},
...
]
}
]
The PlatformRoute model has several useful options:
property | description |
---|---|
children: Array<PlatformRoute> |
The children property defines subsections that will inherit the url path from the parent. |
element: JSX.Element |
The element property is the JSX element that should appear at the specified URL. |
disabled?: boolean |
When a route is marked as disabled, it will not be registered and will the URL will return a 404. |
authRequired?: boolean |
Requiring authentication for a route means that users who are not logged in will be redirected to the Login Form when they try to access the route. |
route: string |
The route property is the path to the route, relative to its parent(s). |
title: string |
The title property is the used for routes identification |
rolesRequired: Array<string> |
Requiring roles for a route means that users who do not own the roles will be presented with restricted page when they try to access the route. |
Typescript rules: src/.eslintrc.js Javascript rules are set as "overrides" under the same linting rules: src/.eslintrc.js
% yarn lint
% yarn lint:fix
See the yarn commmands for further options.
VS Code has several plugins and settings that make linting easy.
The most useful feature is to automatically apply all lint rules any time you save a file.
-
Code → Preferences → Settings
-
Search for “save” to find the setting
- Editor: Code Actions on Save
-
Click the “Edit in settings.json” link
-
Add the following config:
{ ... "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": true, }, }
Created by Microsoft, this plugin will allow you to see lint errors in the Problems panel.
WARNING: Other lint plugins can interfere with ESLint, so it is recommended that you uninstall/disable all other lint plugins (e.g. TSLint, Prettier, etc).
We use Sass for styling, which is a preprocessor scripting language that compiles to CSS and allows for the use of variables, nested rules, mixins, functions, etc.
Variables can be used to store any CSS value that you want to reuse throughout your stylesheets. Variables are prefixed with the $ symbol.
e.g. styles.scss
$primary-color: #333;
body {
color: $primary-color;
}
Mixins let you create groups of CSS declarations that you want to reuse throughout your site. You can also pass in values to make your mixin more flexible, and you call them using @include
.
e.g. styles.scss
@mixin theme($theme: DarkGray) {
background: $theme;
color: #fff;
}
.info {
@include theme;
}
.alert {
@include theme($theme: DarkRed);
}
Shared stylesheets are located in src/libs/ui/lib/styles/
.
We use variables and mixins for handling padding, colors and breakpoints in the application, among others. To reference these in your SCSS files, simply add the needed lines at the top of your file.
@import '@libs/ui/styles/includes';
@import '@libs/ui/styles/typography';
@import '@libs/ui/styles/variables';
Colors and Gradients are defined as variables in src/libs/ui/lib/styles/_palette.scss
.
WARNING: Do not use any colors that are not already defined in the palette. If a mockup you are working from has a different color, find the color in the palette that is closest.
Padding for various screen sizes are defined as variables in src/libs/ui/lib/styles/_layout.scss
.
This file also contains a mixin called pagePaddings
that determines the correct padding to use for the current screen size based on breakpoints.
Breakpoint mixins are defined in src/libs/ui/lib/styles/_breakpoints.scss
and can be used to apply different styling based on the screen width.
Here is an example that applies a different height value than the default to a css class selector if the screen is considered small (376px - 464px).
_breakpoints.scss
$sm-min: 376px;
$sm-max: 464px;
@mixin sm {
@media (min-width: #{$sm-min}) and (max-width: #{$sm-max}){
@content;
}
}
example.scss
@import '../lib/styles';
.example {
height: 100px;
@include sm {
height: 50px;
}
}
Mobile UIs use xs, sm, and md breakpoints. Larger breakpoints are desktop UIs.
For specifying mobile CSS, you can use @include ltemd:
.exampleDesktopContent {
display: flex;
width: 100%;
flex-direction: column;
@include ltemd {
flex-direction: row;
}
}
WARNING: Do not add any breakpoints!
We use the SVG icons library Heroicons, where each icon is available in an outline
or solid
version.
We import both sets of icons in the file src/libs/ui/lib/components/svgs/index.ts
.
import * as IconOutline from '@heroicons/react/outline'
import * as IconSolid from '@heroicons/react/solid'
Then, to use an icon from either of these sets, you would import the corresponding set into your JSX file and reference the icon of your choice as a component:
e.g.:
import { IconOutline } from '~/libs/ui'
...
<IconOutline.InformationCircleIcon width={28} height={28} />
Custom SVGs can also be imported and used directly as a React component. Save your SVG in its own index (i.e. "barrel" file within your app (e.g. /src/apps/my-app/src/lib/svgs), and then import the SVG into the barrel file as a component:
import { ReactComponent as CustomSVG } from './customSvg.svg'
The export the svg from the barrel file to be used w/in your app:
export { CustomSVG }
See the /src/libs/ui/lib/components/svgs for an example.
NOTE: Custom SVGs should be saved w/in a given app. Only global SVGs should be in the main /src/libs/ui/lib/components/svgs directory.
You can style an SVG icon by overwritting its properties through CSS (height, width, fill, etc.).
There are also existing mixins located in src/libs/ui/lib/styles/_icons.scss
with pre-defined widths and heights for various icon sizes.
e.g.:
.logo-link {
svg {
width: calc($space-xxl + $space-xxxxl);
height: $space-xl;
fill: none;
path {
fill: $tc-white;
}
}
}
.no-logo-link {
svg {
@include icon-lg;
}
}
NOTE - all SVGs require explicit
width
andheight
in the Safari browser in order to be rendered properly, otherwise they'll be rendered to the default size and probably will crop out of view
The following summarizes the various apps in the Platform UI.
This is the "router" app under the whole sum of all Platform UI applications. It will just load all applications and serve one based on the specific route It also renders the Universal Navigation's header & footer.
Platform README Platform Routes
A community-led project to document how to work with Topcoder internal applications.
Dev Center README Dev Center Routes
The application that displays the list of challenges & gigs: opportunity feed
Application that allows administrators to CRUD badges and de/assign them to specific users.
Gamification Admin README TBD Gamification Admin Routes
Application that serves 3rd-party educational content.
Application that allows customers to submit/start challenges self-service.