Improve polls: option lengths & redesign (#13257)

This commit redesign the polls and increases characters limit for the
options from 25 to 50 characters, giving pollsters more freedom.

Summarizing, the redesign is making the polls more adaptive for upcoming
changes to the options characters limit: the bar, or a "chart", is now
displayed separately from the option itself; vote check mark is moved
next to the option text, making the percentages take less space. Option
lengths are taken into account and text is wrapped to multiple lines
if necessary to avoid overflow.
This commit is contained in:
Sasha Sorokin 2020-04-02 22:10:55 +07:00 committed by GitHub
parent a9a063c0e9
commit 37b3985bfa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 48 additions and 38 deletions

View file

@ -127,15 +127,7 @@ class Poll extends ImmutablePureComponent {
return ( return (
<li key={option.get('title')}> <li key={option.get('title')}>
{showResults && ( <label className={classNames('poll__option', { selectable: !showResults })}>
<Motion defaultStyle={{ width: 0 }} style={{ width: spring(percent, { stiffness: 180, damping: 12 }) }}>
{({ width }) =>
<span className={classNames('poll__chart', { leading })} style={{ width: `${width}%` }} />
}
</Motion>
)}
<label className={classNames('poll__text', { selectable: !showResults })}>
<input <input
name='vote-options' name='vote-options'
type={poll.get('multiple') ? 'checkbox' : 'radio'} type={poll.get('multiple') ? 'checkbox' : 'radio'}
@ -157,12 +149,26 @@ class Poll extends ImmutablePureComponent {
/> />
)} )}
{showResults && <span className='poll__number'> {showResults && <span className='poll__number'>
{!!voted && <Icon id='check' className='poll__vote__mark' title={intl.formatMessage(messages.voted)} />}
{Math.round(percent)}% {Math.round(percent)}%
</span>} </span>}
<span dangerouslySetInnerHTML={{ __html: titleEmojified }} /> <span
className='poll__option__text'
dangerouslySetInnerHTML={{ __html: titleEmojified }}
/>
{!!voted && <span className='poll__voted'>
<Icon id='check' className='poll__voted__mark' title={intl.formatMessage(messages.voted)} />
</span>}
</label> </label>
{showResults && (
<Motion defaultStyle={{ width: 0 }} style={{ width: spring(percent, { stiffness: 180, damping: 12 }) }}>
{({ width }) =>
<span className={classNames('poll__chart', { leading })} style={{ width: `${width}%` }} />
}
</Motion>
)}
</li> </li>
); );
} }

View file

@ -75,7 +75,7 @@ class Option extends React.PureComponent {
return ( return (
<li> <li>
<label className='poll__text editable'> <label className='poll__option editable'>
<span <span
className={classNames('poll__input', { checkbox: isPollMultiple })} className={classNames('poll__input', { checkbox: isPollMultiple })}
onClick={this.handleToggleMultiple} onClick={this.handleToggleMultiple}
@ -88,7 +88,7 @@ class Option extends React.PureComponent {
<AutosuggestInput <AutosuggestInput
placeholder={intl.formatMessage(messages.option_placeholder, { number: index + 1 })} placeholder={intl.formatMessage(messages.option_placeholder, { number: index + 1 })}
maxLength={25} maxLength={50}
value={title} value={title}
onChange={this.handleOptionTitleChange} onChange={this.handleOptionTitleChange}
suggestions={this.props.suggestions} suggestions={this.props.suggestions}

View file

@ -142,7 +142,7 @@ html {
} }
.compose-form__autosuggest-wrapper, .compose-form__autosuggest-wrapper,
.poll__text input[type="text"], .poll__option input[type="text"],
.compose-form .spoiler-input__input, .compose-form .spoiler-input__input,
.compose-form__poll-wrapper select, .compose-form__poll-wrapper select,
.search__input, .search__input,

View file

@ -8,20 +8,18 @@
} }
&__chart { &__chart {
position: absolute;
top: 0;
left: 0;
height: 100%;
display: inline-block;
border-radius: 4px; border-radius: 4px;
background: darken($ui-primary-color, 14%); display: block;
background: darken($ui-primary-color, 5%);
height: 5px;
min-width: 1%;
&.leading { &.leading {
background: $ui-highlight-color; background: $ui-highlight-color;
} }
} }
&__text { &__option {
position: relative; position: relative;
display: flex; display: flex;
padding: 6px 0; padding: 6px 0;
@ -29,6 +27,13 @@
cursor: default; cursor: default;
overflow: hidden; overflow: hidden;
&__text {
display: inline-block;
word-wrap: break-word;
overflow-wrap: break-word;
max-width: calc(100% - 45px - 25px);
}
input[type=radio], input[type=radio],
input[type=checkbox] { input[type=checkbox] {
display: none; display: none;
@ -112,19 +117,18 @@
&__number { &__number {
display: inline-block; display: inline-block;
width: 52px; width: 45px;
font-weight: 700; font-weight: 700;
padding: 0 10px; flex: 0 0 45px;
padding-left: 8px;
text-align: right;
margin-top: auto;
margin-bottom: auto;
flex: 0 0 52px;
} }
&__vote__mark { &__voted {
float: left; padding: 0 5px;
line-height: 18px; display: inline-block;
&__mark {
font-size: 18px;
}
} }
&__footer { &__footer {
@ -199,7 +203,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
.poll__text { .poll__option {
flex: 0 0 auto; flex: 0 0 auto;
width: calc(100% - (23px + 6px)); width: calc(100% - (23px + 6px));
margin-right: 6px; margin-right: 6px;

View file

@ -2,7 +2,7 @@
class PollValidator < ActiveModel::Validator class PollValidator < ActiveModel::Validator
MAX_OPTIONS = 4 MAX_OPTIONS = 4
MAX_OPTION_CHARS = 25 MAX_OPTION_CHARS = 50
MAX_EXPIRATION = 1.month.freeze MAX_EXPIRATION = 1.month.freeze
MIN_EXPIRATION = 5.minutes.freeze MIN_EXPIRATION = 5.minutes.freeze

View file

@ -8,16 +8,16 @@
%li %li
- if show_results - if show_results
- percent = total_votes_count > 0 ? 100 * option.votes_count / total_votes_count : 0 - percent = total_votes_count > 0 ? 100 * option.votes_count / total_votes_count : 0
%span.poll__chart{ style: "width: #{percent}%" } %label.poll__option><
%label.poll__text><
%span.poll__number>< %span.poll__number><
- if own_votes.include?(index) - if own_votes.include?(index)
%i.poll__vote__mark.fa.fa-check %i.poll__voted__mark.fa.fa-check
= percent.round = percent.round
= Formatter.instance.format_poll_option(status, option, autoplay: autoplay) = Formatter.instance.format_poll_option(status, option, autoplay: autoplay)
%span.poll__chart{ style: "width: #{percent}%" }
- else - else
%label.poll__text>< %label.poll__option><
%span.poll__input{ class: poll.multiple? ? 'checkbox' : nil}>< %span.poll__input{ class: poll.multiple? ? 'checkbox' : nil}><
= Formatter.instance.format_poll_option(status, option, autoplay: autoplay) = Formatter.instance.format_poll_option(status, option, autoplay: autoplay)
.poll__footer .poll__footer