Skip to content

Commit 00e482d

Browse files
Merge branch 'develop' into thrive-article-page
2 parents 26fdd9b + 92b91e7 commit 00e482d

File tree

9 files changed

+460
-5
lines changed

9 files changed

+460
-5
lines changed

.circleci/config.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ workflows:
343343
branches:
344344
only:
345345
- develop
346+
- fix-challenge-end-date
346347
# This is alternate dev env for parallel testing
347348
- "build-test":
348349
context : org-global
@@ -356,7 +357,7 @@ workflows:
356357
filters:
357358
branches:
358359
only:
359-
- thrive-article-page
360+
- free
360361
# This is beta env for production soft releases
361362
- "build-prod-beta":
362363
context : org-global
@@ -371,7 +372,7 @@ workflows:
371372
branches:
372373
only:
373374
- develop
374-
- tc-api-issue
375+
- fix-challenge-end-date
375376
# Production builds are exectuted
376377
# when PR is merged to the master
377378
# Don't change anything in this configuration
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/**
2+
* Member talk cloud component
3+
*/
4+
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
5+
/* eslint-disable jsx-a11y/click-events-have-key-events */
6+
/* eslint-disable prefer-destructuring */
7+
import _ from 'lodash';
8+
import PT from 'prop-types';
9+
import React from 'react';
10+
import { themr } from 'react-css-super-themr';
11+
import { fixStyle } from 'utils/contentful';
12+
import defaultTheme from './themes/default.scss';
13+
14+
const MAX_MARGIN_TOP = 0;
15+
const MIN_MARGIN_LEFT = -20;
16+
const MAX_MARGIN_LEFT = 30;
17+
18+
const getRandomTranslate = () => ({
19+
y: MAX_MARGIN_TOP,
20+
x: _.random(MIN_MARGIN_LEFT, MAX_MARGIN_LEFT, false),
21+
});
22+
23+
export class MemberTalkCloud extends React.Component {
24+
constructor(props) {
25+
super(props);
26+
27+
this.state = {
28+
selectedMember: 0,
29+
context: _.map(props.content, item => ({ ...item, margins: getRandomTranslate() })),
30+
};
31+
32+
this.onSelect = this.onSelect.bind(this);
33+
this.getData = this.getData.bind(this);
34+
}
35+
36+
onSelect(selectedMember) {
37+
this.setState({ selectedMember });
38+
}
39+
40+
getData(newActiveIndex) {
41+
const { context } = this.state;
42+
const temp = context[newActiveIndex];
43+
context[newActiveIndex] = context[0];
44+
context[0] = temp;
45+
const ITEMS_ON_LEFT_SIDE = Math.floor(context.length / 2);
46+
47+
const [activeBlob] = context;
48+
const leftSide = context.slice(1, ITEMS_ON_LEFT_SIDE + 1);
49+
const rightSide = context
50+
.slice(ITEMS_ON_LEFT_SIDE + 1);
51+
return {
52+
activeBlob,
53+
leftSide,
54+
rightSide,
55+
};
56+
}
57+
58+
render() {
59+
const { theme, extraStylesForContainer, id } = this.props;
60+
const { selectedMember, context } = this.state;
61+
const { activeBlob, leftSide, rightSide } = this.getData(selectedMember);
62+
const ITEMS_ON_LEFT_SIDE = Math.floor(context.length / 2);
63+
const {
64+
entry,
65+
active,
66+
blob,
67+
left,
68+
right,
69+
} = theme;
70+
71+
return (
72+
<div id={id} className={theme.container} style={fixStyle(extraStylesForContainer)}>
73+
<div className={left}>
74+
{_.map(leftSide, (item, index) => (
75+
<div
76+
className={entry}
77+
key={index}
78+
style={{
79+
transform: `translate(${item.margins.x}px, ${item.margins.y}px)`,
80+
}}
81+
>
82+
<img
83+
alt={item.text}
84+
src={item.smallImageURL}
85+
onClick={() => this.onSelect(index + 1)}
86+
/>
87+
<button onClick={() => this.onSelect(index + 1)} type="button" style={{ color: item.handleColor }}>{item.handle}</button>
88+
</div>
89+
))}
90+
</div>
91+
<div className={`${entry} ${active}`}>
92+
<img
93+
alt={activeBlob.text}
94+
src={activeBlob.imageURL}
95+
key={Math.random()}
96+
/>
97+
<span className={theme.activeHandle}>{activeBlob.handle}</span>
98+
<div className={blob}>
99+
<span>{`"${activeBlob.text}"`}</span>
100+
{activeBlob.ReadMoreURL && <a href={activeBlob.ReadMoreURL}>{activeBlob.ReadMoreText || 'Read More'}</a>}
101+
</div>
102+
</div>
103+
<div className={right}>
104+
{_.map(rightSide, (item, index) => (
105+
<div
106+
className={entry}
107+
key={index}
108+
style={{
109+
transform: `translate(${item.margins.x}px, ${item.margins.y}px)`,
110+
}}
111+
>
112+
<img
113+
alt={item.text}
114+
src={item.smallImageURL}
115+
onClick={() => this.onSelect(index + ITEMS_ON_LEFT_SIDE + 1)}
116+
/>
117+
<button onClick={() => this.onSelect(index + ITEMS_ON_LEFT_SIDE + 1)} type="button" style={{ color: item.handleColor }}>{item.handle}</button>
118+
</div>
119+
))}
120+
</div>
121+
</div>
122+
);
123+
}
124+
}
125+
126+
127+
MemberTalkCloud.defaultProps = {
128+
content: [],
129+
extraStylesForContainer: null,
130+
};
131+
132+
MemberTalkCloud.propTypes = {
133+
theme: PT.shape({
134+
container: PT.string.isRequired,
135+
entry: PT.string.isRequired,
136+
active: PT.string.isRequired,
137+
blob: PT.string.isRequired,
138+
left: PT.string.isRequired,
139+
right: PT.string.isRequired,
140+
activeHandle: PT.string.isRequired,
141+
}).isRequired,
142+
content: PT.arrayOf(PT.shape({
143+
smllImageURL: PT.string.isRequired,
144+
imageURL: PT.string.isRequired,
145+
text: PT.string.isRequired,
146+
ReadMoreURL: PT.string,
147+
ReadMoreText: PT.string,
148+
handle: PT.string.isRequired,
149+
handleColor: PT.string.isRequired,
150+
})),
151+
extraStylesForContainer: PT.shape(),
152+
id: PT.string.isRequired,
153+
};
154+
155+
export default themr('Member-Talk-Cloud', defaultTheme)(MemberTalkCloud);
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Member Card component.
3+
*/
4+
5+
import ContentfulLoader from 'containers/ContentfulLoader';
6+
import LoadingIndicator from 'components/LoadingIndicator';
7+
import PT from 'prop-types';
8+
import React from 'react';
9+
import _ from 'lodash';
10+
11+
// eslint-disable-next-line import/no-named-as-default
12+
import MemberTalkCloud from './MemberTalkCloud';
13+
14+
/* Loads the main member card entry. */
15+
export default function MemberTalkCloudLoader(props) {
16+
const {
17+
id, preview, spaceName, environment,
18+
} = props;
19+
return (
20+
<ContentfulLoader
21+
entryIds={id}
22+
preview={preview}
23+
spaceName={spaceName}
24+
environment={environment}
25+
render={(cloudData) => {
26+
const { fields } = cloudData.entries.items[id];
27+
return (
28+
<ContentfulLoader
29+
entryIds={_.map(fields.items, 'sys.id')}
30+
preview={preview}
31+
spaceName={spaceName}
32+
environment={environment}
33+
render={cloudItemsData => (
34+
<MemberTalkCloud
35+
id={id}
36+
content={_.map(cloudItemsData.entries.items, item => ({
37+
smallImageURL: item.fields.avatarSmall.fields.file.url,
38+
imageURL: item.fields.avatar.fields.file.url,
39+
text: item.fields.text || item.fields.name,
40+
ReadMoreURL: item.fields.linkURL,
41+
ReadMoreText: item.fields.linkText,
42+
handle: item.fields.handle,
43+
handleColor: item.fields.handleColor,
44+
}))}
45+
extraStylesForContainer={fields.extraStylesForContainer}
46+
/>
47+
)}
48+
renderPlaceholder={LoadingIndicator}
49+
/>
50+
);
51+
}}
52+
renderPlaceholder={LoadingIndicator}
53+
/>
54+
);
55+
}
56+
57+
MemberTalkCloudLoader.defaultProps = {
58+
preview: false,
59+
spaceName: null,
60+
environment: null,
61+
};
62+
63+
MemberTalkCloudLoader.propTypes = {
64+
id: PT.string.isRequired,
65+
preview: PT.bool,
66+
spaceName: PT.string,
67+
environment: PT.string,
68+
};

0 commit comments

Comments
 (0)