Skip to content

Commit cc8b277

Browse files
Merge branch 'dev' into TCA-499_eslint
2 parents b77b646 + 27f9c60 commit cc8b277

File tree

124 files changed

+2274
-753
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

124 files changed

+2274
-753
lines changed

README.md

Lines changed: 122 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,75 @@ All future user interfaces at Topcoder will be implemented here. Pre-existing us
66

77
>**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.
88
9-
# Getting started with local development
9+
- [Source Control & CI/CD](#source-control--cicd)
10+
- [Local Development](#local-development)
11+
- [Application Structure](#application-structure)
12+
- [Coding Practices](#coding-practices)
13+
- [Tools](#tools)
14+
15+
---
16+
17+
# Source Control & CI/CD
1018

11-
- [Local Environment Setup](#local-environment-setup)
1219
- [Deployments](#deployments)
13-
- [Developer Center specific setup](#developer-center-contentful-api-key-and-space-id)
14-
- [Yarn Commands](#yarn-commands)
20+
- [Pull Requests](#pull-requests)
21+
- [Branching](#branching)
22+
- [Commits](#commits)
1523

16-
# Application structure
24+
## Deployments
1725

18-
- [Folder Structure](#folder-structure)
19-
- [Adding a Tool or Util](#adding-a-tool-or-util)
26+
The app uses CircleCI for CI/CD.
2027

21-
# Coding Practices
22-
- [Git](#git)
23-
- [Linting](#linting)
24-
- [Styling](#styling)
25-
- [Icons](#icons)
28+
The `dev` branch is auto-deployed to the dev environment: https://platform-mvp.topcoder-dev.com.
2629

27-
---
30+
The `master` branch is auto-deployed to the production environment: https://platform-ui.topcoder.com.
31+
32+
## Pull Requests
33+
34+
If a Jira ticket requires any code changes, it should have its own pull request.
35+
36+
PRs should be named as follows:
37+
38+
`[TICKET-###] [Short Description] -> [target-branch-name]`
39+
40+
e.g. `GAME-174 Upload Badge Image Fix -> dev`
41+
42+
PRs should also have a description that includes a link to the Jira ticket and a summary of what the PR is changing.
43+
44+
## Branching
45+
46+
All branches use `dev` as their source. All merges to `dev` should be made via [pull request](#pull-requests) and should be approved by application owner(s).
47+
48+
When working on Jira tickets, a branch should correspond with a single ticket.
49+
50+
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`.
51+
52+
Use the following naming convention for branches in order to link associated Git PRs and branches to the tickets:
53+
54+
`[TICKET-###]_[short-description]`
55+
56+
e.g.: `PROD-1516_work-issue`
57+
58+
## Commits
59+
We use [Smart Commits](https://bigbrassband.com/git-integration-for-jira/documentation/smart-commits.html#bbb-nav-basic-examples) to link comments and time tracking to tickets. You would enter the following as your commit message:
60+
61+
`[TICKET #] #comment <commit message> #time <jira-formatted time>`
62+
63+
e.g.: `PROD-001 #comment adding readme notes #time 45m`
64+
65+
66+
67+
68+
69+
70+
71+
72+
73+
# Local Development
74+
75+
- [Local Environment Setup](#local-environment-setup)
76+
- [Tool-specific Setup](#tool-specific-setup)
77+
- [Yarn Commands](#yarn-commands)
2878

2979
## Local Environment Setup
3080

@@ -127,28 +177,10 @@ Otherwise, you will need to override the exception each time you load the site.
127177
3. Set the REACT_APP_HOST_ENV=[hostname]
128178
4. Add "start:[hostname]": "sh start-ssl-[hostname].sh" to scripts in package.json
129179

130-
## Deployments
131-
132-
The app uses CircleCI for CI/CD.
133-
134-
The "dev" branch is auto-deployed to the dev environment: https://platform-mvp.topcoder-dev.com.
135-
136-
The "master" branch is auto-deployed to the production environment: https://platform-ui.topcoder.com.
137-
138-
## Developer Center Contentful API Key and Space Id
139-
140-
The app requires two environment variables, which contain the space id and the key used to access contentful and retrieve Thrive Articles.
141-
142-
You should create a file named `.env` in the root folder, and write inside the following lines:
143-
144-
```sh
145-
REACT_APP_CONTENTFUL_EDU_SPACE_ID=<space-id>
146-
REACT_APP_CONTENTFUL_EDU_CDN_API_KEY=<API Key>
147-
```
148180

149-
We should use the same space ID and API Key as Topcoder Thrive, these are for fetching Thrive articles and videos in the landing page.
181+
## Tool-specific setup
150182

151-
See the [Dev Center README](/src-ts/tools/dev-center/README.md) for further instructions on setting up the Dev Center.
183+
Each [Tool](#tools) can have its own setup requirements. Please see each tool's [README](#tools) for further information.
152184

153185
## yarn Commands
154186

@@ -169,6 +201,15 @@ See the [Dev Center README](/src-ts/tools/dev-center/README.md) for further inst
169201
| `yarn report:coverage`| Generate e2e coverage report in html format |
170202
| `yarn report:coverage:text` | Generate e2e coverage report in text format |
171203

204+
205+
206+
207+
208+
# Application Structure
209+
210+
- [Folder Structure](#folder-structure)
211+
- [Adding a Tool or Util](#adding-a-tool-or-util)
212+
172213
## Folder Structure
173214

174215
The folder structure of the app has the following goals:
@@ -317,25 +358,14 @@ The PlatformRoute model has several useful options:
317358
| `title: string` | The title property is the text that will appear in the Tools or Utils Selectors (this is irrelevant on hidden routes). |
318359
| `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. |
319360

320-
## Git
321-
322-
### Branching
323-
When working on Jira tickets, we link associated Git PRs and branches to the tickets. Use the following naming convention for branches:
324-
325-
`[TICKET #]_short-description`
326-
327-
e.g.: `PROD-1516_work-issue`
328-
329-
#### Branching strategy
330-
TBD
331361

332-
### Commits
333-
We use [Smart Commits](https://bigbrassband.com/git-integration-for-jira/documentation/smart-commits.html#bbb-nav-basic-examples) to link comments and time tracking to tickets. You would enter the following as your commit message:
334362

335-
`[TICKET #] #comment <commit message> #time <jira-formatted time>`
336363

337-
e.g.: `PROD-001 #comment adding readme notes #time 45m`
338364

365+
# Coding Practices
366+
- [Linting](#linting)
367+
- [Styling](#styling)
368+
- [Icons](#icons)
339369

340370
## Linting
341371

@@ -549,3 +579,47 @@ e.g.:
549579
```
550580
551581
>**NOTE** - all SVGs require explicit `width` and `height` 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
582+
583+
584+
585+
586+
587+
588+
589+
590+
# Tools
591+
592+
The following summarizes the various [tools](#adding-a-tool-or-util) in the Platform UI.
593+
594+
- [Dev Center](#dev-center)
595+
- [Gamification Admin](#gamification-admin)
596+
- [Learn](#learn)
597+
- [Work](#work)
598+
599+
## Dev Center
600+
601+
A community-led project to document how to work with Topcoder internal applications.
602+
603+
[Dev Center README](./src-ts/tools/dev-center/README.md)
604+
[Dev Center Routes](./src-ts/tools/dev-center/dev-center.routes.tsx)
605+
606+
## Gamification Admin
607+
608+
Application that allows administrators to CRUD badges and de/assign them to specific users.
609+
610+
[Gamification Admin README TBD](./src-ts/tools/gamification-admin/README.md)
611+
[Gamification Admin Routes](./src-ts/tools/gamification-admin/gamification-admin.routes.tsx)
612+
613+
## Learn
614+
615+
Application that serves 3rd-party educational content.
616+
617+
[Learn README](./src-ts/tools/learn/README.md)
618+
[Learn Routes](./src-ts/tools/learn/learn.routes.tsx)
619+
620+
## Work
621+
622+
Application that allows customers to submit/start challenges self-service.
623+
624+
[Work README TBD](./src-ts/tools/work/README.md)
625+
[Work Routes](./src-ts/tools/work/work.routes.tsx)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@topcoder-platform/platform-ui",
3-
"version": "0.1.0",
3+
"version": "2.1",
44
"private": true,
55
"scripts": {
66
"dev": "yarn react-app-rewired start",
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
tc_handle,badge_id,badge_name,awarded_by,awarded_at
2+
kirildev,858dafad-5fcd-4bc3-ab7c-849453d139ad,,kirildev,2022-08-15T07:25:43.187Z
3+
jcori,bc39b152-e0e3-4984-962b-f1eba67229dd,TCO22 Development Finalist,,2022-08-15T07:25:43.187Z
4+
amy_admin,bc39b152-e0e3-4984-962b-f1eba67229dd,TCO22 Development Finalist,,

src-ts/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ export {
2323
} from './lib'
2424
export * from './tools'
2525
export * from './utils'
26+
export * from './lib/svgs'

src-ts/lib/button/Button.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export interface ButtonProps {
1616
readonly elementType?: keyof JSX.IntrinsicElements
1717
readonly hidden?: boolean
1818
readonly icon?: FC<SVGProps<SVGSVGElement>>
19+
readonly id?: string
1920
readonly label?: string
2021
readonly name?: string
2122
readonly onClick?: (event?: any) => void
@@ -79,6 +80,8 @@ const Button: FC<ButtonProps> = (props: ButtonProps) => {
7980
tabIndex={props.tabIndex}
8081
title={props.title}
8182
type={props.type || 'button'}
83+
id={props.id}
84+
value={props.id}
8285
>
8386
{content}
8487
</button>

src-ts/lib/contact-support-form/ContactSupportForm.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { FC, useContext } from 'react'
1+
import { Dispatch, FC, SetStateAction, useContext, useEffect, useState } from 'react'
22

33
import { Form, FormDefinition, formGetInputModel, FormInputModel } from '../form'
4+
import { LoadingSpinner } from '../loading-spinner'
45
import { profileContext, ProfileContextData } from '../profile-provider'
56

67
import { ContactSupportFormField } from './contact-support-form.config'
@@ -18,6 +19,15 @@ const ContactSupportForm: FC<ContactSupportFormProps> = (props: ContactSupportFo
1819

1920
const { profile }: ProfileContextData = useContext(profileContext)
2021

22+
const [loading, setLoading]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(false)
23+
const [saveOnSuccess, setSaveOnSuccess]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(false)
24+
25+
useEffect(() => {
26+
if (!loading && saveOnSuccess) {
27+
props.onSave()
28+
}
29+
}, [loading, saveOnSuccess])
30+
2131
function generateRequest(inputs: ReadonlyArray<FormInputModel>): ContactSupportRequest {
2232
const firstName: string = formGetInputModel(inputs, ContactSupportFormField.first).value as string
2333
const lastName: string = formGetInputModel(inputs, ContactSupportFormField.last).value as string
@@ -34,10 +44,11 @@ const ContactSupportForm: FC<ContactSupportFormProps> = (props: ContactSupportFo
3444
}
3545

3646
async function saveAsync(request: ContactSupportRequest): Promise<void> {
47+
setLoading(true)
3748
return contactSupportSubmitRequestAsync(request)
3849
.then(() => {
39-
props.onSave()
40-
})
50+
setSaveOnSuccess(true)
51+
}).finally(() => setLoading(false))
4152
}
4253

4354
const emailElement: JSX.Element | undefined = !!profile?.email
@@ -50,6 +61,7 @@ const ContactSupportForm: FC<ContactSupportFormProps> = (props: ContactSupportFo
5061

5162
return (
5263
<>
64+
<LoadingSpinner hide={!loading} type='Overlay' />
5365
<div className={styles['contact-support-intro']}>
5466
<p>
5567
Hi {profile?.firstName || 'there'}, we're here to help.

src-ts/lib/form/Form.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ interface FormProps<ValueType, RequestType> {
3838
readonly resetFormAfterSave?: boolean
3939
readonly resetFormOnUnmount?: boolean
4040
readonly save: (value: RequestType) => Promise<void>
41+
readonly shouldDisableButton?: (isPrimaryGroup: boolean, index: number) => boolean
4142
}
4243

4344
const Form: <ValueType extends any, RequestType extends any>(props: FormProps<ValueType, RequestType>) => JSX.Element
@@ -146,21 +147,29 @@ const Form: <ValueType extends any, RequestType extends any>(props: FormProps<Va
146147

147148
formInitializeValues(inputs, props.formValues)
148149

150+
const setOnClickOnReset: (button: FormButton) => FormButton = (button) => {
151+
// if this is a reset button, set its onclick to reset
152+
if (!!button.isReset) {
153+
button = {
154+
...button,
155+
onClick: onReset,
156+
}
157+
}
158+
159+
return button
160+
}
161+
149162
const createButtonGroup: (groups: ReadonlyArray<FormButton>, isPrimaryGroup: boolean) => Array<JSX.Element> = (groups, isPrimaryGroup) => {
150163
return groups.map((button, index) => {
151-
// if this is a reset button, set its onclick to reset
152-
if (!!button.isReset) {
153-
button = {
154-
...button,
155-
onClick: onReset,
156-
}
157-
}
164+
button = setOnClickOnReset(button)
165+
166+
const disabled: boolean = (button.isSubmit && isFormInvalid) || !!props.shouldDisableButton?.(isPrimaryGroup, index)
158167

159168
return (
160169
<Button
161170
{...button}
162171
key={button.label || `button-${index}`}
163-
disable={button.isSubmit && isFormInvalid}
172+
disable={disabled}
164173
tabIndex={button.notTabble ? -1 : index + (inputs ? inputs.length : 0) + (formDef.tabIndexStart || 0)}
165174
/>
166175
)

src-ts/lib/form/form-button.model.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import { ButtonSize, ButtonStyle, ButtonType } from '../button'
55
export interface FormButton {
66
readonly buttonStyle?: ButtonStyle
77
hidden?: boolean,
8-
readonly icon?: FC<SVGProps<SVGSVGElement>>
8+
icon?: FC<SVGProps<SVGSVGElement>>
99
readonly isReset?: boolean
1010
readonly isSubmit?: boolean
11-
readonly label?: string
11+
label?: string
1212
readonly notTabble?: boolean
1313
onClick?: (event?: any) => void
1414
readonly route?: string

src-ts/lib/form/form-functions/form.functions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export async function onSubmitAsync<T>(
7272
): Promise<void> {
7373

7474
event.preventDefault()
75+
event.stopPropagation()
7576

7677
const { groups, shortName, successMessage }: FormDefinition = formDef
7778
const inputs: Array<FormInputModel> = getFormInputFields(groups || [])

0 commit comments

Comments
 (0)