Skip to content

Commit 969dd12

Browse files
authored
Merge pull request #134 from topcoder-platform/develop
Updated header links
2 parents e7154a3 + 4344572 commit 969dd12

File tree

12 files changed

+8629
-55
lines changed

12 files changed

+8629
-55
lines changed

__tests__/shared/actions/challenge.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import actions from 'actions/challenge';
22

3+
jest.mock('services/challenges');
4+
35
const mockFetch = resolvesTo => jest.fn(() =>
46
Promise.resolve({ json: () => resolvesTo }));
57

@@ -51,13 +53,18 @@ describe('challenge.getDetailsDone', () => {
5153
expect(a.type).toBe('CHALLENGE/GET_DETAILS_DONE');
5254
});
5355

54-
/* TODO: This test does not work anymore, as the action was refactored to
55-
* use challenges service for API v3 calls, while API v2 calls still happen
56-
* the way they used to. Thus, we need a better mock of fetch to test it,
57-
* or some alternative approach. */
58-
test.skip('payload is a promise which resolves to the expected object', () =>
59-
a.payload.then(res => expect(res).toEqual(
60-
['DUMMY DATA', { result: { content: ['DUMMY DATA'] } }, {}])));
56+
const mockChallenge =
57+
require('services/__mocks__/data/challenges-v3.json').result.content[0];
58+
59+
test('payload is a promise which resolves to the expected object', () =>
60+
a.payload.then(res => expect(res).toEqual([
61+
mockChallenge, {
62+
result: {
63+
content: ['DUMMY DATA'],
64+
},
65+
}, undefined,
66+
])),
67+
);
6168
});
6269

6370

__tests__/shared/components/tc-communities/__snapshots__/Header.jsx.snap

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,14 @@ exports[`Snapshot match 2`] = `
131131
<div
132132
className="tc-communities__header__logos src-shared-components-tc-communities-Header-___style__logos___38nqG"
133133
>
134-
<Connect(RRLinkWrapper)
134+
<span
135135
className="tc-communities__header__logo src-shared-components-tc-communities-Header-___style__logo___z2xG6"
136-
to="pageId1"
137136
>
138137
<img
139-
alt="Community logo"
138+
alt="logo"
140139
src="some/logo/url"
141140
/>
142-
</Connect(RRLinkWrapper)>
141+
</span>
143142
</div>
144143
<div
145144
className="tc-communities__header__challenge-dropdown src-shared-components-tc-communities-Header-___style__challenge-dropdown___3fQW9"

docs/how-to-add-a-new-topcoder-community.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,14 @@ To add a new community with the name **demo**, we should follow the following pr
8181
- `communitySelector` - *Object Array* - Specifies data for the community selection dropdown inside the community header. Each object MUST HAVE `label` and `value` string fields, and MAY HAVE `redirect` field. If `redirect` field is specified, a click on that option in the dropdown will redirect user to the specified URL.
8282
- `groupId` - *String* - This value of group ID is now used to fetch community statistics. Probably, it makes sense to use this value everywhere where `authorizedGroupIds` array is used, however, at the moment, these two are independent.
8383
- `leaderboardApiUrl` - *String* - Endpoint from where the leaderboard data should be loaded.
84-
- `logo` - *String Array* - Array of image URLs to insert as logos into the left corner of community's header.
84+
- `logos` - *String Array | Object Array* - Array of image URLs to insert as logos into the left corner of community's header, alternatively the array may contain JS objects of shape
85+
```
86+
{
87+
"img": "<SOME-IMAGE-URL>",
88+
"url": "https://www.topcoder.com"
89+
}
90+
```
91+
For such elements `img` will be used as the image source, and `url` will be the redirection URL triggered by a click on the logo.
8592
- `additionalLogos` - *String Array* - Array of image URLs to insert as logos into the right corner of community's header.
8693
- `hideSearch` - *Boolean* - Hide/Show the search icon.
8794
- `chevronOverAvatar` - *Boolean* - Render a *chevron-down* instead of the user avatar.

src/server/tc-communities/community-2/metadata.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121
}],
2222
"groupId": "20000002",
2323
"leaderboardApiUrl": "https://api.topcoder.com/v4/looks/458/run/json/",
24-
"logos": [
25-
"/themes/community-2/wipro-logo.png",
26-
"/themes/community-2/logo_topcoder_with_name.svg"
27-
],
24+
"logos": [{
25+
"img": "/themes/community-2/logo_topcoder_with_name.svg",
26+
"url": "https://www.topcoder.com"
27+
}],
2828
"menuItems": [
2929
{
3030
"title": "Home",

src/server/tc-communities/demo-expert/metadata.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818
"value": "3"
1919
}],
2020
"groupId": "1001",
21-
"logos": [
22-
"/themes/demo-expert/logo_topcoder_with_name.svg"
23-
],
21+
"logos": [{
22+
"img": "/themes/demo-expert/logo_topcoder_with_name.svg",
23+
"url": "https://www.topcoder.com"
24+
}],
2425
"menuItems": [
2526
{
2627
"title": "Home",

src/server/tc-communities/taskforce/metadata.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
}],
2222
"groupId": "20000003",
2323
"leaderboardApiUrl": "https://api.topcoder.com/v4/looks/458/run/json/",
24-
"logos": [
25-
"/themes/taskforce/logo_topcoder_with_name.svg"
26-
],
24+
"logos": [{
25+
"img": "/themes/taskforce/logo_topcoder_with_name.svg",
26+
"url": "https://www.topcoder.com"
27+
}],
2728
"menuItems": [
2829
{
2930
"title": "Home",

src/server/tc-communities/tc-prod-dev/metadata.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
"value": "3"
1818
}],
1919
"groupId": "20000001",
20-
"logos": [
21-
"/themes/wipro/logo_topcoder_with_name.svg"
22-
],
20+
"logos": [{
21+
"img": "/themes/wipro/logo_topcoder_with_name.svg",
22+
"url": "https://www.topcoder.com"
23+
}],
2324
"menuItems": [
2425
{
2526
"title": "Home",

src/server/tc-communities/wipro/metadata.json

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,19 @@
1111
"label": "TopGear Community",
1212
"value": "1"
1313
}, {
14-
"label": "Cognitive Community",
15-
"redirect": "http://cognitive.topcoder.com/",
14+
"label": "Topcoder Public Community",
15+
"redirect": "https://www.topcoder.com",
1616
"value": "2"
17-
}, {
18-
"label": "iOS Community",
19-
"redirect": "https://ios.topcoder.com/",
20-
"value": "3"
2117
}],
2218
"groupId": "20000000",
2319
"leaderboardApiUrl": "https://api.topcoder.com/v4/looks/458/run/json/",
24-
"logos": [
25-
"/themes/wipro/wipro-logo.png",
26-
"/themes/wipro/logo_topcoder_with_name.svg"
27-
],
20+
"logos": [{
21+
"img": "/themes/wipro/wipro-logo.png",
22+
"url": "http://www.wipro.com/"
23+
}, {
24+
"img": "/themes/wipro/logo_topcoder_with_name.svg",
25+
"url": "https://www.topcoder.com"
26+
}],
2827
"additionalLogos": [
2928
"/themes/wipro/topgear_logo.png"
3029
],

src/shared/components/tc-communities/Header/index.jsx

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,30 @@ export default function Header(props) {
6161
};
6262
}
6363

64+
const renderedLogos = logos.map((item) => {
65+
const img = _.isString(item) ? item : item.img;
66+
let logo = <img alt="logo" src={_.isString(item) ? item : item.img} />;
67+
if (_.isObject(item) && item.url) {
68+
logo = (
69+
<Link
70+
key={img}
71+
to={item.url}
72+
styleName="logo"
73+
className="tc-communities__header__logo"
74+
>{logo}</Link>
75+
);
76+
} else {
77+
logo = (
78+
<span
79+
key={img}
80+
styleName="logo"
81+
className="tc-communities__header__logo"
82+
>{logo}</span>
83+
);
84+
}
85+
return logo;
86+
});
87+
6488
const loginState = profile ? (
6589
<div
6690
onMouseEnter={event => openMenu(userSubMenu, event.target)}
@@ -127,26 +151,7 @@ export default function Header(props) {
127151
</button>
128152
<div styleName="logos-wrap">
129153
<div styleName="logos" className="tc-communities__header__logos">
130-
{_.map(logos, (logoUrl, index) =>
131-
(menuItems.length ? (
132-
<Link
133-
key={index}
134-
to={menuItems[0].url}
135-
styleName="logo"
136-
className="tc-communities__header__logo"
137-
>
138-
<img src={logoUrl} alt="Community logo" />
139-
</Link>
140-
) : (
141-
<span
142-
key={index}
143-
styleName="logo"
144-
className="tc-communities__header__logo"
145-
>
146-
<img src={logoUrl} alt="Community logo" />
147-
</span>
148-
)),
149-
)}
154+
{renderedLogos}
150155
</div>
151156

152157
<div
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/**
2+
* This module provides a service for conventient access to Topcoder APIs.
3+
*/
4+
5+
import _ from 'lodash';
6+
import 'isomorphic-fetch'; /* global fetch */
7+
import config from 'utils/config';
8+
9+
/**
10+
* API service object. It is reused for both Topcoder API v2 and v3,
11+
* as in these cases we are fine with the same interface, and the only
12+
* thing we need to be different is the base URL and auth token to use.
13+
*/
14+
export default class Api {
15+
/**
16+
* @param {String} base Base URL of the API.
17+
* @param {String} token Optional. Authorization token.
18+
*/
19+
constructor(base, token) {
20+
this.private = { base, token };
21+
}
22+
23+
/**
24+
* Sends a request to the specified endpoint of the API. This method just
25+
* wraps fetch() in a convenient way. If this object was created with the
26+
* auth token, it will be automatically added to auth header of all
27+
* requests.
28+
* For additional details see https://github.github.io/fetch/
29+
* @param {String} enpoint Should start with slash, like /endpoint.
30+
* @param {Object} options Optional. Fetch options.
31+
* @return {Promise} It resolves to the HTTP response object. To get the
32+
* actual data you probably want to call .json() method of that object.
33+
* Mind that this promise rejects only on network errors. In case of
34+
* HTTP errors (404, etc.) the promise will be resolved successfully,
35+
* and you should check .status or .ok fields of the response object
36+
* to find out the response status.
37+
*/
38+
fetch(endpoint, options) {
39+
const p = this.private;
40+
const headers = { 'Content-Type': 'application/json' };
41+
if (p.token) headers.Authorization = `Bearer ${p.token}`;
42+
const ops = _.merge(_.cloneDeep(options) || {}, { headers });
43+
return fetch(`${p.base}${endpoint}`, ops);
44+
}
45+
46+
/**
47+
* Sends DELETE request to the specified endpoint.
48+
* @param {String} endpoint
49+
* @param {Blob|BufferSource|FormData|String} body
50+
* @return {Promise}
51+
*/
52+
delete(endpoint, body) {
53+
return this.fetch(endpoint, { body, method: 'DELETE' });
54+
}
55+
56+
/**
57+
* Sends GET request to the specified endpoint.
58+
* @param {String} endpoint
59+
* @return {Promise}
60+
*/
61+
get(endpoint) {
62+
return this.fetch(endpoint);
63+
}
64+
65+
/**
66+
* Sends POST request to the specified endpoint.
67+
* @param {String} endpoint
68+
* @param {Blob|BufferSource|FormData|String} body
69+
* @return {Promise}
70+
*/
71+
post(endpoint, body) {
72+
return this.fetch(endpoint, { body, method: 'POST' });
73+
}
74+
75+
/**
76+
* Sends POST request to the specified endpoint, with JSON payload.
77+
* @param {String} endpoint
78+
* @param {JSON} json
79+
* @return {Promise}
80+
*/
81+
postJson(endpoint, json) {
82+
return this.post(endpoint, JSON.stringify(json));
83+
}
84+
85+
/**
86+
* Sends PUT request to the specified endpoint.
87+
* @param {String} endpoint
88+
* @param {Blob|BufferSource|FormData|String} body
89+
* @return {Promise}
90+
*/
91+
put(endpoint, body) {
92+
return this.fetch(endpoint, { body, method: 'PUT' });
93+
}
94+
95+
/**
96+
* Sends PUT request to the specified endpoint.
97+
* @param {String} endpoint
98+
* @param {JSON} json
99+
* @return {Promise}
100+
*/
101+
putJson(endpoint, json) {
102+
return this.put(endpoint, JSON.stringify(json));
103+
}
104+
}
105+
106+
/**
107+
* Topcoder API v2.
108+
*/
109+
110+
/**
111+
* Returns a new or existing Api object for Topcoder API v2.
112+
* @param {String} token Optional. Auth token for Topcoder API v2.
113+
* @return {Api} API v2 service object.
114+
*/
115+
let lastApiV2 = null;
116+
export function getApiV2(token) {
117+
if (!lastApiV2 || lastApiV2.private.token !== token) {
118+
lastApiV2 = new Api(config.API.V2, token);
119+
}
120+
return lastApiV2;
121+
}
122+
123+
/**
124+
* Topcoder API v3.
125+
*/
126+
127+
/**
128+
* Returns a new or existing Api object for Topcoder API v3
129+
* @param {String} token Optional. Auth token for Topcoder API v3.
130+
* @return {Api} API v3 service object.
131+
*/
132+
let lastApiV3 = null;
133+
export function getApiV3(token) {
134+
if (!lastApiV3 || lastApiV3.private.token !== token) {
135+
lastApiV3 = new Api(config.API.V3, token);
136+
}
137+
return lastApiV3;
138+
}

0 commit comments

Comments
 (0)