convert polling component

This commit is contained in:
Ramón Souza 2021-11-04 17:03:25 +00:00
parent e37208b8ce
commit 396cacffad
5 changed files with 298 additions and 260 deletions

View File

@ -1,13 +1,10 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Button from '/imports/ui/components/button/component';
import injectWbResizeEvent from '/imports/ui/components/presentation/resize-wrapper/component';
import { defineMessages, injectIntl } from 'react-intl';
import cx from 'classnames';
import { Meteor } from 'meteor/meteor';
import { styles } from './styles.scss';
import Styled from './styles';
import AudioService from '/imports/ui/components/audio/service';
import Checkbox from '/imports/ui/components/checkbox/component';
const MAX_INPUT_CHARS = Meteor.settings.public.poll.maxTypedAnswerLength;
@ -113,7 +110,7 @@ class Polling extends Component {
}
}
renderButtonAnswers(pollAnswerStyles) {
renderButtonAnswers() {
const {
isMeteorConnected,
intl,
@ -131,7 +128,7 @@ class Polling extends Component {
if (!poll) return null;
const { answers, question, pollType } = poll;
const { stackOptions, answers, question, pollType } = poll;
const defaultPoll = isDefaultPoll(pollType);
return (
@ -141,12 +138,12 @@ class Polling extends Component {
<span>
{
question.length === 0 && (
<div className={styles.pollingTitle}>
<Styled.PollingTitle>
{intl.formatMessage(intlMessages.pollingTitleLabel)}
</div>
</Styled.PollingTitle>
)
}
<div className={cx(pollAnswerStyles)}>
<Styled.PollingAnswers removeColumns={answers.length === 1} stacked={stackOptions}>
{answers.map((pollAnswer) => {
const formattedMessageIndex = pollAnswer.key.toLowerCase();
let label = pollAnswer.key;
@ -155,13 +152,9 @@ class Polling extends Component {
}
return (
<div
key={pollAnswer.id}
className={styles.pollButtonWrapper}
>
<Button
<Styled.PollButtonWrapper key={pollAnswer.id} >
<Styled.PollingButton
disabled={!isMeteorConnected}
className={styles.pollingButton}
color="primary"
size="md"
label={label}
@ -171,30 +164,24 @@ class Polling extends Component {
aria-describedby={`pollAnswerDesc${pollAnswer.key}`}
data-test="pollAnswerOption"
/>
<div
className={styles.hidden}
id={`pollAnswerLabel${pollAnswer.key}`}
>
<Styled.Hidden id={`pollAnswerLabel${pollAnswer.key}`}>
{intl.formatMessage(intlMessages.pollAnswerLabel, { 0: label })}
</div>
<div
className={styles.hidden}
id={`pollAnswerDesc${pollAnswer.key}`}
>
</Styled.Hidden>
<Styled.Hidden id={`pollAnswerDesc${pollAnswer.key}`}>
{intl.formatMessage(intlMessages.pollAnswerDesc, { 0: label })}
</div>
</div>
</Styled.Hidden>
</Styled.PollButtonWrapper>
);
})}
</div>
</Styled.PollingAnswers>
</span>
)
}
{
poll.pollType === pollTypes.Response
&& (
<div className={styles.typedResponseWrapper}>
<input
<Styled.TypedResponseWrapper>
<Styled.TypedResponseInput
data-test="pollAnswerOption"
onChange={(e) => {
this.handleUpdateResponseInput(e);
@ -203,14 +190,12 @@ class Polling extends Component {
this.handleMessageKeyDown(e);
}}
type="text"
className={styles.typedResponseInput}
placeholder={intl.formatMessage(intlMessages.responsePlaceholder)}
maxLength={MAX_INPUT_CHARS}
ref={(r) => { this.responseInput = r; }}
/>
<Button
<Styled.SubmitVoteButton
data-test="submitAnswer"
className={styles.submitVoteBtn}
disabled={typedAns.length === 0}
color="primary"
size="sm"
@ -220,12 +205,12 @@ class Polling extends Component {
handleTypedVote(poll.pollId, typedAns);
}}
/>
</div>
</Styled.TypedResponseWrapper>
)
}
<div className={styles.pollingSecret}>
<Styled.PollingSecret>
{intl.formatMessage(poll.secretPoll ? intlMessages.responseIsSecret : intlMessages.responseNotSecret)}
</div>
</Styled.PollingSecret>
</div>
);
}
@ -243,11 +228,11 @@ class Polling extends Component {
<div>
{question.length === 0
&& (
<div className={styles.pollingTitle}>
<Styled.PollingTitle>
{intl.formatMessage(intlMessages.pollingTitleLabel)}
</div>
</Styled.PollingTitle>
)}
<table className={styles.multipleResponseAnswersTable}>
<Styled.MultipleResponseAnswersTable>
{poll.answers.map((pollAnswer) => {
const formattedMessageIndex = pollAnswer.key.toLowerCase();
let label = pollAnswer.key;
@ -256,39 +241,33 @@ class Polling extends Component {
}
return (
<tr
<Styled.CheckboxContainer
key={pollAnswer.id}
className={styles.checkboxContainer}
>
<td>
<Checkbox
<Styled.PollingCheckbox
disabled={!isMeteorConnected}
id={`answerInput${pollAnswer.key}`}
onChange={() => this.handleCheckboxChange(poll.pollId, pollAnswer.id)}
checked={checkedAnswers.includes(pollAnswer.id)}
className={styles.checkbox}
ariaLabelledBy={`pollAnswerLabel${pollAnswer.key}`}
ariaDescribedBy={`pollAnswerDesc${pollAnswer.key}`}
/>
</td>
<td className={styles.multipleResponseAnswersTableAnswerText}>
<Styled.MultipleResponseAnswersTableAnswerText>
<label id={`pollAnswerLabel${pollAnswer.key}`}>
{label}
</label>
<div
className={styles.hidden}
id={`pollAnswerDesc${pollAnswer.key}`}
>
<Styled.Hidden id={`pollAnswerDesc${pollAnswer.key}`} >
{intl.formatMessage(intlMessages.pollAnswerDesc, { 0: label })}
</div>
</td>
</tr>
</Styled.Hidden>
</Styled.MultipleResponseAnswersTableAnswerText>
</Styled.CheckboxContainer>
);
})}
</table>
</Styled.MultipleResponseAnswersTable>
<div>
<Button
className={styles.submitVoteBtn}
<Styled.SubmitVoteButton
disabled={!isMeteorConnected || checkedAnswers.length === 0}
color="primary"
size="sm"
@ -309,36 +288,28 @@ class Polling extends Component {
if (!poll) return null;
const { stackOptions, answers, question } = poll;
const pollAnswerStyles = {
[styles.pollingAnswers]: true,
[styles.removeColumns]: answers.length === 1,
[styles.stacked]: stackOptions,
};
const { stackOptions, question } = poll;
return (
<div className={styles.overlay}>
<div
<Styled.Overlay>
<Styled.PollingContainer
autoWidth={stackOptions}
data-test="pollingContainer"
className={cx({
[styles.pollingContainer]: true,
[styles.autoWidth]: stackOptions,
})}
role="alert"
>
{
question.length > 0 && (
<span className={styles.qHeader}>
<div className={styles.qTitle}>
<Styled.QHeader>
<Styled.QTitle>
{intl.formatMessage(intlMessages.pollQuestionTitle)}
</div>
<div data-test="pollQuestion" className={styles.qText}>{question}</div>
</span>
</Styled.QTitle>
<Styled.QText data-test="pollQuestion">{question}</Styled.QText>
</Styled.QHeader>
)
}
{poll.isMultipleResponse ? this.renderCheckboxAnswers(pollAnswerStyles) : this.renderButtonAnswers(pollAnswerStyles)}
</div>
</div>
{poll.isMultipleResponse ? this.renderCheckboxAnswers() : this.renderButtonAnswers()}
</Styled.PollingContainer>
</Styled.Overlay>
);
}
}

View File

@ -0,0 +1,236 @@
import styled from 'styled-components';
import Checkbox from '/imports/ui/components/checkbox/component';
import {
mdPaddingY,
smPaddingY,
jumboPaddingY,
smPaddingX,
borderRadius,
pollWidth,
pollSmMargin,
overlayIndex,
overlayOpacity,
pollIndex,
lgPaddingY,
pollBottomOffset,
jumboPaddingX,
pollColAmount,
borderSize,
} from '/imports/ui/stylesheets/styled-components/general';
import {
fontSizeSmall,
fontSizeBase,
fontSizeLarge,
} from '/imports/ui/stylesheets/styled-components/typography';
import {
colorText,
colorBlueLight,
colorGrayLighter,
colorOffWhite,
colorGrayDark,
colorWhite,
colorPrimary,
} from '/imports/ui/stylesheets/styled-components/palette';
import { hasPhoneDimentions } from '/imports/ui/stylesheets/styled-components/breakpoints';
import Button from '/imports/ui/components/button/component';
const PollingTitle = styled.div`
white-space: nowrap;
padding-bottom: ${mdPaddingY};
padding-top: ${mdPaddingY};
font-size: ${fontSizeSmall};
`;
const PollButtonWrapper = styled.div`
text-align: center;
padding: ${smPaddingY};
width: 100%;
`;
const PollingButton = styled(Button)`
width: 100%;
max-width: 9em;
@media ${hasPhoneDimentions} {
max-width: none;
}
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`;
const Hidden = styled.div`
display: none;
`;
const TypedResponseWrapper = styled.div`
margin: ${jumboPaddingY} .5rem .5rem .5rem;
display: flex;
flex-flow: column;
`;
const TypedResponseInput = styled.input`
&:focus {
outline: none;
border-radius: ${borderSize};
box-shadow: 0 0 0 ${borderSize} ${colorBlueLight}, inset 0 0 0 1px ${colorPrimary};
}
color: ${colorText};
-webkit-appearance: none;
padding: calc(${smPaddingY} * 2.5) calc(${smPaddingX} * 1.25);
border-radius: ${borderRadius};
font-size: ${fontSizeBase};
border: 1px solid ${colorGrayLighter};
box-shadow: 0 0 0 1px ${colorGrayLighter};
margin-bottom: 1rem;
`;
const SubmitVoteButton = styled(Button)`
font-size: ${fontSizeBase};
`;
const PollingSecret = styled.div`
font-size: ${fontSizeSmall};
max-width: ${pollWidth};
`;
const MultipleResponseAnswersTable = styled.table`
margin-left: auto;
margin-right: auto;
`;
const PollingCheckbox = styled(Checkbox)`
display: inline-block;
margin-right: ${pollSmMargin};
`;
const CheckboxContainer = styled.tr`
margin-bottom: ${pollSmMargin};
`;
const MultipleResponseAnswersTableAnswerText = styled.td`
text-align: left;
`;
const Overlay = styled.div`
position: absolute;
height: 100vh;
width: 100vw;
z-index: ${overlayIndex};
pointer-events: none;
@media ${hasPhoneDimentions} {
pointer-events: auto;
background-color: rgba(0, 0, 0, ${overlayOpacity});
}
`;
const QHeader = styled.span`
text-align: left;
position: relative;
left: ${smPaddingY};
`;
const QTitle = styled.div`
font-size: ${fontSizeSmall};
`;
const QText = styled.div`
color: ${colorText};
word-break: break-word;
white-space: pre-wrap;
font-size: ${fontSizeLarge};
max-width: ${pollWidth};
padding-right: ${smPaddingX};
`;
const PollingContainer = styled.div`
pointer-events:auto;
min-width: ${pollWidth};
position: absolute;
z-index: ${pollIndex};
border: 1px solid ${colorOffWhite};
border-radius: ${borderRadius};
box-shadow: ${colorGrayDark} 0px 0px ${lgPaddingY};
align-items: center;
text-align: center;
font-weight: 600;
padding: ${mdPaddingY};
background-color: ${colorWhite};
bottom: ${pollBottomOffset};
right: ${jumboPaddingX};
[dir="rtl"] & {
left: ${jumboPaddingX};
right: auto;
}
@media ${hasPhoneDimentions} {
bottom: auto;
right: auto;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
max-height: 95%;
overflow-y: auto;
[dir="rtl"] & {
left: 50%;
}
}
${({ autoWidth }) => autoWidth && `
width: auto;
`}
`;
const PollingAnswers = styled.div`
display: grid;
grid-template-columns: repeat(${pollColAmount}, 1fr);
@media ${hasPhoneDimentions} {
grid-template-columns: repeat(1, 1fr);
& div button {
grid-column: 1;
}
}
z-index: 1;
${({ removeColumns }) => removeColumns && `
grid-template-columns: auto;
`}
${({ stacked }) => stacked && `
grid-template-columns: repeat(1, 1fr);
& div button {
max-width: none !important;
}
`}
`;
export default {
PollingTitle,
PollButtonWrapper,
PollingButton,
Hidden,
TypedResponseWrapper,
TypedResponseInput,
SubmitVoteButton,
PollingSecret,
MultipleResponseAnswersTable,
PollingCheckbox,
CheckboxContainer,
MultipleResponseAnswersTableAnswerText,
Overlay,
QHeader,
QTitle,
QText,
PollingContainer,
PollingAnswers,
};

View File

@ -1,185 +0,0 @@
@import "/imports/ui/stylesheets/mixins/focus";
@import "../../stylesheets/variables/breakpoints";
:root {
--col-amount: 2;
--max-btn-width: 9em;
--overlayIndex: 9999;
--overlayOpacity: 0.349;
--poll-index: 1016;
--poll-width: 18rem;
--poll-bottom-offset: 4.5rem;
}
.overlay {
position: absolute;
height: 100vh;
width: 100vw;
z-index: var(--overlayIndex);
pointer-events: none;
@include mq($hasPhoneDimentions) {
pointer-events: auto;
background-color: rgba(0, 0, 0, var(--overlayOpacity));
}
}
.pollingContainer {
pointer-events:auto;
min-width: var(--poll-width);
position: absolute;
z-index: var(--poll-index);
border: 1px solid var(--color-off-white);
border-radius: var(--border-radius);
box-shadow: var(--color-gray-dark) 0px 0px var(--lg-padding-y);
align-items: center;
text-align: center;
font-weight: 600;
padding: var(--md-padding-y);
background-color: var(--color-white);
bottom: var(--poll-bottom-offset);
right: var(--jumbo-padding-x);
[dir="rtl"] & {
left: var(--jumbo-padding-x);
right: auto;
}
@include mq($hasPhoneDimentions) {
bottom: auto;
right: auto;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
max-height: 95%;
overflow-y: auto;
[dir="rtl"] & {
left: 50%;
}
}
}
.pollingTitle {
white-space: nowrap;
padding-bottom: var(--md-padding-y);
padding-top: var(--md-padding-y);
font-size: var(--font-size-small);
}
.pollButtonWrapper {
text-align: center;
padding: var(--sm-padding-y);
width: 100%;
}
.pollingButton {
width: 100%;
max-width: var(--max-btn-width);
@include mq($hasPhoneDimentions) {
max-width: none;
}
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.pollingAnswers {
display: grid;
grid-template-columns: repeat(var(--col-amount), 1fr);
@include mq($hasPhoneDimentions) {
grid-template-columns: repeat(1, 1fr);
.pollButtonWrapper button {
grid-column: 1;
}
}
z-index: 1;
}
.stacked {
grid-template-columns: repeat(1, 1fr);
.pollButtonWrapper button {
max-width: none !important;
}
}
.removeColumns {
grid-template-columns: auto;
}
.autoWidth {
width: auto;
}
.hidden {
display: none;
}
.checkbox {
display: inline-block;
margin-right: var(--poll-sm-margin);
}
.checkboxContainer {
margin-bottom: var(--poll-sm-margin);
}
.qHeader {
text-align: left;
position: relative;
left: var(--sm-padding-y);
}
.qTitle {
font-size: var(--font-size-small);
}
.qText {
color: var(--color-text);
word-break: break-word;
white-space: pre-wrap;
font-size: var(--font-size-large);
max-width: var(--poll-width);
padding-right: var(--sm-padding-x);
}
.typedResponseWrapper {
margin: var(--jumbo-padding-y) .5rem .5rem .5rem;
display: flex;
flex-flow: column;
}
.submitVoteBtn {
font-size: var(--font-size-base);
}
.typedResponseInput {
@include inputFocus(var(--color-blue-light));
color: var(--color-text);
-webkit-appearance: none;
padding: calc(var(--sm-padding-y) * 2.5) calc(var(--sm-padding-x) * 1.25);
border-radius: var(--border-radius);
font-size: var(--font-size-base);
border: 1px solid var(--color-gray-lighter);
box-shadow: 0 0 0 1px var(--color-gray-lighter);
margin-bottom: 1rem;
}
.multipleResponseAnswersTable {
margin-left: auto;
margin-right: auto;
}
.multipleResponseAnswersTableAnswerText {
text-align: left;
}
.pollingSecret {
font-size: var(--font-size-small);
max-width: var(--poll-width);
}

View File

@ -3,6 +3,7 @@ const mediumOnly = 'only screen and (min-width: 40.063em) and (max-width: 64em)'
const mediumUp = 'only screen and (min-width: 40.063em)';
const landscape = "only screen and (orientation: landscape)";
const phoneLandscape = 'only screen and (max-width: 480px) and (orientation: landscape)';
const hasPhoneDimentions = 'only screen and (max-height: 479px), only screen and (max-width: 479px)';
export {
smallOnly,
@ -10,4 +11,5 @@ export {
mediumUp,
landscape,
phoneLandscape,
hasPhoneDimentions,
};

View File

@ -9,6 +9,7 @@ const mdPaddingX = '1rem';
const lgPaddingX = '1.25rem';
const lgPaddingY = '0.6rem';
const jumboPaddingY = '1.5rem';
const jumboPaddingX = '3.025rem';
const whiteboardToolbarPadding = '.5rem';
const minModalHeight = '20rem';
@ -47,6 +48,12 @@ const pollSmMargin = '0.3125rem';
const pollMdMargin = '0.7rem';
const pollResultWidth = '15rem';
const pollInputHeight = '2.5rem';
const pollWidth = '18rem';
const overlayIndex = '9999';
const overlayOpacity = '0.349';
const pollIndex = '1016';
const pollBottomOffset = '4.5rem';
const pollColAmount = '2';
export {
borderSizeSmall,
@ -60,6 +67,7 @@ export {
lgPaddingX,
lgPaddingY,
jumboPaddingY,
jumboPaddingX,
whiteboardToolbarPadding,
minModalHeight,
descriptionMargin,
@ -94,4 +102,10 @@ export {
pollMdMargin,
pollResultWidth,
pollInputHeight,
pollWidth,
overlayIndex,
overlayOpacity,
pollIndex,
pollBottomOffset,
pollColAmount,
};