Skip to content

Commit ac24a9a

Browse files
committed
merge with master
2 parents 000362c + b40344c commit ac24a9a

Some content is hidden

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

44 files changed

+4229
-7067
lines changed

.nvmrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
16.20.1
1+
18.13.0

.storybook/main.js

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { merge } from 'webpack-merge'
22
const webpack = require('webpack')
3-
const path = require('path')
43

54
const config = {
65
stories: [
@@ -9,20 +8,19 @@ const config = {
98
addons: [
109
'@storybook/addon-links',
1110
'@storybook/addon-essentials',
12-
'@storybook/addon-onboarding',
1311
'@storybook/addon-interactions',
1412
'@storybook/addon-a11y',
1513
'@storybook/addon-viewport',
14+
'@storybook/addon-webpack5-compiler-babel',
1615
],
1716
framework: {
1817
name: '@storybook/react-webpack5',
1918
options: {},
2019
},
21-
docs: {
22-
autodocs: 'tag',
23-
},
20+
docs: {},
2421
webpackFinal: async config => {
2522
const newConfig = merge(config, {
23+
devtool: 'source-map',
2624
module: {
2725
rules: [
2826
{

app/components/round-tracker.js

+29-29
Original file line numberDiff line numberDiff line change
@@ -38,40 +38,40 @@ const RoundTracker = ({ roundsStatus = [], className, ...otherProps }) => {
3838
return <div className={classes.emptyMessage}>No rounds available</div>
3939
}
4040

41-
let visibleRounds
4241
const currentRoundIndex = roundsStatus.findIndex(status => status === 'inProgress')
4342

44-
if (isMobile) {
45-
if (currentRoundIndex === 0) {
46-
visibleRounds = roundsStatus.slice(0, 2) // Show the first two rounds
47-
} else if (currentRoundIndex === roundsStatus.length - 1) {
48-
visibleRounds = roundsStatus.slice(-1) // Show only the last round
49-
} else {
50-
visibleRounds = roundsStatus.slice(currentRoundIndex, currentRoundIndex + 2) // Show the current and next round
51-
}
43+
let lowerIndex, upperIndex
44+
45+
if (currentRoundIndex === 0) {
46+
lowerIndex = 0
47+
upperIndex = isMobile ? 2 : 3 // Show the first two rounds on mobile, first 3 on full-width
48+
} else if (currentRoundIndex === roundsStatus.length - 1) {
49+
lowerIndex = roundsStatus.length - (isMobile ? 1 : 3)
50+
upperIndex = roundsStatus.length - 1 // Show only the last round on mobile, last 3 on full-width
5251
} else {
53-
if (currentRoundIndex <= 0) {
54-
visibleRounds = roundsStatus.slice(0, 3) // Show the first three rounds
55-
} else if (currentRoundIndex === roundsStatus.length - 1) {
56-
visibleRounds = roundsStatus.slice(-3) // Show the last three rounds
57-
} else {
58-
visibleRounds = roundsStatus.slice(currentRoundIndex - 1, currentRoundIndex + 2) // Show the previous, current, and next round
59-
}
52+
// Show the current and next round on mobile.
53+
// Previous, current, and next round on full-width
54+
lowerIndex = isMobile ? currentRoundIndex : currentRoundIndex - 1
55+
upperIndex = currentRoundIndex + 2
6056
}
6157

62-
return visibleRounds.map((status, index) => (
63-
<React.Fragment key={index}>
64-
<div className={classes.roundContainer}>
65-
<div className={classes.roundNumber}>Round {roundsStatus.length - visibleRounds.length + index + 1}</div>
66-
<StatusBadge
67-
name={status.charAt(0).toUpperCase() + status.slice(1)}
68-
status={status.toLowerCase()}
69-
className={classes.badge}
70-
/>
71-
</div>
72-
{index < visibleRounds.length - 1 && <div className={classes.dash} />}
73-
</React.Fragment>
74-
))
58+
return roundsStatus.map((status, index) => {
59+
// Only render visible rounds
60+
if (lowerIndex <= index && index < upperIndex)
61+
return (
62+
<React.Fragment key={index}>
63+
<div className={classes.roundContainer}>
64+
<div className={classes.roundNumber}>Round {index + 1}</div>
65+
<StatusBadge
66+
name={status.charAt(0).toUpperCase() + status.slice(1)}
67+
status={status.toLowerCase()}
68+
className={classes.badge}
69+
/>
70+
</div>
71+
{index < upperIndex - 1 && <div className={classes.dash} />}
72+
</React.Fragment>
73+
)
74+
})
7575
}
7676

7777
return (

app/components/step-slider.jsx

+12-5
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,17 @@ export const StepSlider = props => {
9898
case 'clearSendDoneToParent':
9999
return { ...state, sendDoneToParent: false }
100100
case 'updateStatuses':
101-
let { valid, index } = action.payload
101+
let { result, index } = action.payload
102102
if (steps) {
103103
const stepStatuses = state.stepStatuses.map((stepStatus, i) => {
104-
if (valid) return i === index ? { ...stepStatus, complete: true } : stepStatus
104+
if (result.valid || result.valid === undefined) {
105+
return i === index ? { ...stepStatus, complete: true } : stepStatus
106+
}
105107
// Disable navigation to all steps after if invalid
106108
else return i >= state.currentStep ? { ...stepStatus, complete: false } : stepStatus
107109
})
108110
return { ...state, stepStatuses: stepStatuses }
109-
} else if (valid) {
111+
} else if (result) {
110112
// Just increment if no steps
111113
const currentStep = Math.min(state.currentStep + 1, children.length - 1)
112114
return {
@@ -121,7 +123,12 @@ export const StepSlider = props => {
121123
}
122124
// Keep track of each step's seen/completion status
123125
// Populate statuses with initial values
124-
if (steps) steps[0].seen = true
126+
if (steps) {
127+
steps[0].seen = true
128+
steps.forEach((step, index) => {
129+
steps[index].complete = false
130+
})
131+
}
125132
const [state, dispatch] = useReducer(reducer, { currentStep: 0, sendDoneToParent: false, stepStatuses: steps })
126133

127134
// the children need to be cloned to have the onDone function applied, but we don't want to redo this every time we re-render
@@ -135,7 +142,7 @@ export const StepSlider = props => {
135142
...child.props,
136143
key: [index],
137144
onDone: valid => {
138-
dispatch({ type: 'updateStatuses', payload: { valid: valid, index: index } })
145+
dispatch({ type: 'updateStatuses', payload: { result: valid, index: index } })
139146
},
140147
})
141148
),

app/components/tournament.js

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// https://github.com/EnCiv/civil-pursuit/issues/151
2+
3+
import React, { useReducer } from 'react'
4+
import { createUseStyles } from 'react-jss'
5+
import cx from 'classnames'
6+
7+
import StepBar from './step-bar'
8+
import RoundTracker from './round-tracker'
9+
import StepSlider from './step-slider'
10+
import AnswerStep from './answer-step'
11+
import GroupingStep from './grouping-step'
12+
import RankStep from './rank-step'
13+
import ReviewPointList from './review-point-list'
14+
import WhyStep from './why-step'
15+
import CompareReasons from './compare-reasons'
16+
import Intermission from './intermission'
17+
18+
const WebComponents = {
19+
StepBar: StepBar,
20+
RoundTracker: RoundTracker,
21+
StepSlider: StepSlider,
22+
Answer: AnswerStep,
23+
GroupingStep: GroupingStep,
24+
RankStep: RankStep,
25+
ReviewPointList: ReviewPointList,
26+
WhyStep: WhyStep,
27+
CompareReasons: CompareReasons,
28+
Intermission: Intermission,
29+
}
30+
31+
function buildChildren(steps, round) {
32+
return steps.map(step => {
33+
const { webComponent, ...props } = step
34+
35+
const LookupResult = WebComponents[webComponent]
36+
if (LookupResult) {
37+
// Pass all props from step obj except WebComponent
38+
return <LookupResult {...props} round={round} />
39+
} else {
40+
console.error(`Couldn't render step - component '${webComponent}' was not found in WebComponents.`)
41+
return <div>Couldn't render step - component '{webComponent}' was not found in WebComponents.</div>
42+
}
43+
})
44+
}
45+
46+
function Tournament(props) {
47+
const { className, steps = [], ...otherProps } = props
48+
const classes = useStylesFromThemeFunction(props)
49+
50+
function reducer(state, action) {
51+
switch (action.type) {
52+
case 'incrementRound':
53+
// Rebuild steps when going to next round
54+
const newRound = state.currentRound + 1
55+
56+
return {
57+
...state,
58+
currentRound: newRound,
59+
stepComponents: buildChildren(steps, newRound),
60+
}
61+
}
62+
}
63+
64+
const [state, dispatch] = useReducer(reducer, { currentRound: 1, stepComponents: buildChildren(steps, 1) })
65+
66+
const stepInfo = steps.map(step => {
67+
return {
68+
name: step.stepName,
69+
title: step.stepIntro.description,
70+
}
71+
})
72+
73+
return (
74+
<div className={cx(classes.wrapper, className)} {...otherProps}>
75+
<RoundTracker roundsStatus={['complete', 'complete', 'inProgress', 'pending', 'pending']} />
76+
<StepSlider
77+
key={state.currentRound}
78+
steps={stepInfo}
79+
children={state.stepComponents}
80+
onDone={valid => {
81+
if (valid) dispatch({ type: 'incrementRound' })
82+
}}
83+
/>
84+
</div>
85+
)
86+
}
87+
88+
const useStylesFromThemeFunction = createUseStyles(theme => ({
89+
wrapper: {
90+
width: '100%',
91+
},
92+
}))
93+
94+
export default Tournament

app/dturn/dturn.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ module.exports.putGroupings = putGroupings
474474

475475
function rankMostImportant(discussionId, round, userId, statementId, rank = 1) {
476476
/* this is where we will write it to the database
477-
Rankings.push({statementId,round,ranking: 'most', userId, parentId: discussionId})
477+
Ranks.push({statementId,round,ranking: 'most', userId, parentId: discussionId})
478478
*/
479479
deltaShownItemsRank(discussionId, round, statementId, rank)
480480
Discussions[discussionId].Uitems[userId][round].shownStatementIds[statementId].rank = rank
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,68 @@
11
// https://github.com/EnCiv/civil-pursuit/issues/136
22

33
const { Mongo, Collection } = require('@enciv/mongo-collections')
4-
const Rankings = require('../rankings')
4+
const Ranks = require('../ranks')
55

66
beforeAll(async () => {
77
await Mongo.connect(global.__MONGO_URI__, { useUnifiedTopology: true })
8-
await Rankings.setCollectionProps()
8+
await Ranks.setCollectionProps()
99
})
1010

1111
beforeEach(async () => {
12-
await Rankings.drop()
13-
await Rankings.setCollectionProps()
14-
await Rankings.createIndexes([
15-
{ key: { parentId: 1, userId: 1 }, name: 'unique_parentId_userId_index', unique: true },
16-
])
12+
await Ranks.drop()
13+
await Ranks.setCollectionProps()
14+
await Ranks.createIndexes([{ key: { parentId: 1, userId: 1 }, name: 'unique_parentId_userId_index', unique: true }])
1715
})
1816

1917
afterAll(async () => {
2018
await Mongo.disconnect()
2119
})
2220

23-
describe('Rankings Model', () => {
21+
describe('Ranks Model', () => {
2422
it('should be set up correctly', () => {
25-
expect(Rankings.collectionName).toEqual('rankings')
23+
expect(Ranks.collectionName).toEqual('ranks')
2624
})
2725

2826
it('should insert a valid document', async () => {
2927
const validDoc = {
3028
parentId: 'parent1',
3129
userId: 'user1',
30+
discussionId: 'discussion1',
31+
stage: 'pre',
32+
category: 'category1',
3233
}
33-
const result = await Rankings.insertOne(validDoc)
34+
const result = await Ranks.insertOne(validDoc)
3435
expect(result.acknowledged).toBe(true)
3536
})
3637

3738
it('should not insert an invalid document', async () => {
3839
const invalidDoc = {
3940
userId: 'user1',
4041
}
41-
await expect(Rankings.insertOne(invalidDoc)).rejects.toThrow('Document failed validation')
42+
await expect(Ranks.insertOne(invalidDoc)).rejects.toThrow('Document failed validation')
4243
})
4344

4445
it('should enforce unique indexes', async () => {
4546
const doc1 = {
4647
parentId: 'parent2',
4748
userId: 'user2',
49+
discussionId: 'discussion2',
50+
stage: 'stage2',
51+
category: 'category2',
4852
}
4953
const doc2 = {
5054
parentId: 'parent2',
5155
userId: 'user2',
56+
discussionId: 'discussion2',
57+
stage: 'stage2',
58+
category: 'category2',
5259
}
53-
await Rankings.insertOne(doc1)
54-
await expect(Rankings.insertOne(doc2)).rejects.toThrow('duplicate key error')
60+
await Ranks.insertOne(doc1)
61+
await expect(Ranks.insertOne(doc2)).rejects.toThrow('duplicate key error')
5562
})
5663

5764
it('should have the correct indexes', async () => {
58-
const indexes = await Rankings.indexes()
65+
const indexes = await Ranks.indexes()
5966
expect(indexes).toEqual(expect.arrayContaining([expect.objectContaining({ name: 'unique_parentId_userId_index' })]))
6067
})
6168
})

app/models/rankings.js

-36
This file was deleted.

0 commit comments

Comments
 (0)