Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#6519] label on tiles #4542

Merged
merged 1 commit into from
Oct 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 31 additions & 4 deletions meinberlin/apps/budgeting/assets/BudgetingProposalListItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ListItemStats } from './ListItemStats'

const updatedOnStr = django.gettext('updated on')
const createdOnStr = django.gettext('created on')
const BADGES_LIMIT = 3

export const BudgetingProposalListItem = (props) => {
const { proposal, isVotingPhase, tokenvoteApiUrl } = props
Expand All @@ -21,6 +22,33 @@ export const BudgetingProposalListItem = (props) => {
safeLocale
)}`

const normalizeBadgesData = () => {
const tmpList = []
if (proposal.moderator_feedback) {
tmpList.push({ type: 'modFeedback', value: proposal.moderator_feedback })
}
if (proposal.budget) {
tmpList.push({ type: 'budget', value: proposal.budget })
}
if (proposal.point_label) {
tmpList.push({ type: 'pointLabel', value: proposal.point_label })
}
if (proposal.category) {
tmpList.push({ type: 'category', value: proposal.category })
}
if (proposal.labels && proposal.labels.length !== 0) {
for (let i = 0; i < proposal.labels.length; i++) {
tmpList.push({ type: 'label', value: proposal.labels[i] })
}
}
return tmpList
}

const badges = normalizeBadgesData()
const numOfMoreBadges = badges.length > BADGES_LIMIT
? badges.length - BADGES_LIMIT
: -1

return (
<li className="list-item">
{!isVotingPhase && (
Expand All @@ -34,10 +62,9 @@ export const BudgetingProposalListItem = (props) => {
<a href={proposal.url}>{proposal.name}</a>
</h2>
<ListItemBadges
category={proposal.category}
budget={proposal.budget}
pointLabel={proposal.point_label}
moderatorFeedback={proposal.moderator_feedback}
badges={badges.slice(0, BADGES_LIMIT)}
fuzzylogic2000 marked this conversation as resolved.
Show resolved Hide resolved
numOfMoreBadges={numOfMoreBadges}
proposalUrl={proposal.url}
/>
<div className="list-item__vote">
<div>
Expand Down
83 changes: 54 additions & 29 deletions meinberlin/apps/budgeting/assets/ListItemBadges.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,62 @@ import { intComma } from './helpers'
import { SpacedSpan } from './SpacedSpan'

export const ListItemBadges = props => {
// combine css class names to match different styling, depending
// on if it is moderator_feedback or on of the others (category, label etc.)
const getClass = (badge) => {
const labelClass = 'label label--big'
if (badge.type !== 'modFeedback') {
return labelClass
}
const modTypeClass = `label--${badge.value[0].toLowerCase()}`
return `${labelClass} ${modTypeClass}`
}

// returning the value.name in case of category or label
// in case of budget format as intComma
// in case of modFeedback second item in array
const getBadgeText = (badge) => {
if (badge.type === 'pointLabel') {
return badge.value
} else if (badge.type === 'budget') {
return intComma(badge.value) + '€'
} else if (badge.type === 'modFeedback') {
return badge.value[1]
} else if (badge.type === 'category' || badge.type === 'label') {
return badge.value.name
}
}

// only return icon for pointLabels
const hasPointLabelIcon = (type) => {
if (type !== 'pointLabel') {
return
}
return <i className="fas fa-map-marker-alt" aria-hidden="true" />
}

return (
<div className="list-item__labels">
{props.category && (
<SpacedSpan className="label label--big">
{props.category.name}
</SpacedSpan>
)}
{props.pointLabel && (
<SpacedSpan className="label label--big">
<i className="fas fa-map-marker-alt" aria-hidden="true" />
<SpacedSpan>{props.pointLabel}</SpacedSpan>
</SpacedSpan>
)}
{props.budget > 0 && (
<SpacedSpan className="label label--big">
{intComma(props.budget)}€
</SpacedSpan>
)}
{props.moderatorFeedback[0] && (
<SpacedSpan
className={
'label' +
' label--big' +
' label--' +
props.moderatorFeedback[0].toLowerCase() +
' list-item__label--moderator-feedback'
}
>
{props.moderatorFeedback[1]}
</SpacedSpan>
)}
{props.badges.map((badge, idx) => {
return (
<SpacedSpan
key={`badge_${badge.type}_${idx}`}
className={getClass(badge)}
>
{hasPointLabelIcon(badge.type)}
{getBadgeText(badge)}
</SpacedSpan>
)
})}
{props.numOfMoreBadges >= 0 &&
<SpacedSpan className="u-spacer-left-half">
<a
href={props.proposalUrl}
className="list-item__link"
>
{`${props.numOfMoreBadges} More`}
</a>
</SpacedSpan>}
</div>
)
}
74 changes: 64 additions & 10 deletions meinberlin/apps/budgeting/assets/__tests__/ListItemBadges.jest.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,75 @@ import React from 'react'
import { render, screen } from '@testing-library/react'
import { ListItemBadges } from '../ListItemBadges'

// testing data:
const categoryBadge = [{
type: 'category',
value: { id: 0, name: 'category1' }
}]

const labelsBadges = [
{
type: 'label',
value: { id: 0, name: 'label1' }
},
{
type: 'label',
value: { id: 1, name: 'label2' }
},
{
type: 'label',
value: { id: 2, name: 'label3' }
}
]

const pointLabelBadge = [{
type: 'pointLabel',
value: 'labelwithicon'
}]

const budgetBadge = [{
type: 'budget',
value: '20000'
}]

const modFeedbackBadge = [{
type: 'modFeedback',
value: ['CONSIDERATION', 'Under consideration']
}]

test('displaying category badge', () => {
render(
<ListItemBadges
moderatorFeedback={['CONSIDERATION', 'wird ueberprueft']}
category={{ name: 'Renovation' }}
badges={categoryBadge}
/>
)
expect(screen.getByText('Renovation')).toBeTruthy()
expect(screen.getByText('category1')).toBeTruthy()
})

test('displaying 3 labels', () => {
render(
<ListItemBadges
badges={labelsBadges}
/>
)
expect(screen.getByText('label1')).toBeTruthy()
expect(screen.getByText('label2')).toBeTruthy()
expect(screen.getByText('label3')).toBeTruthy()
})

test('displaying point label badge', () => {
render(
<ListItemBadges
moderatorFeedback={['CONSIDERATION', 'wird ueberprueft']}
pointLabel="Bezirk Ost"
badges={pointLabelBadge}
/>
)
expect(screen.getByText('Bezirk Ost')).toBeTruthy()
expect(screen.getByText('labelwithicon')).toBeTruthy()
})

test('displaying budget badge with thousand separator', () => {
render(
<ListItemBadges
moderatorFeedback={['CONSIDERATION', 'wird ueberprueft']}
budget="20000"
badges={budgetBadge}
/>
)
expect(
Expand All @@ -37,8 +81,18 @@ test('displaying budget badge with thousand separator', () => {
test('displaying moderator feedback badge', () => {
render(
<ListItemBadges
moderatorFeedback={['CONSIDERATION', 'wird ueberprueft']}
badges={modFeedbackBadge}
/>
)
expect(screen.getByText('Under consideration')).toBeTruthy()
})

test('displaying first 3 badges and add more link', () => {
render(
<ListItemBadges
badges={[...categoryBadge, ...labelsBadges]}
numOfMoreBadges={1}
/>
)
expect(screen.getByText('wird ueberprueft')).toBeTruthy()
expect(screen.getByText('1 More')).toBeTruthy()
})
13 changes: 11 additions & 2 deletions meinberlin/apps/budgeting/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class ProposalSerializer(serializers.ModelSerializer):
moderator_feedback = serializers.SerializerMethodField()
session_token_voted = serializers.SerializerMethodField()
vote_allowed = serializers.SerializerMethodField()
budget = serializers.SerializerMethodField()
point_label = serializers.SerializerMethodField()

class Meta:
model = Proposal
Expand Down Expand Up @@ -83,18 +85,25 @@ def get_url(self, proposal):
return proposal.get_absolute_url()

def get_moderator_feedback(self, proposal):
if hasattr(proposal, 'moderator_feedback'):
if hasattr(proposal, 'moderator_feedback') and\
proposal.get_moderator_feedback_display():
return (proposal.moderator_feedback,
proposal.get_moderator_feedback_display())
else:
return None

def get_point_label(self, proposal):
if hasattr(proposal, 'point_label'):
if hasattr(proposal, 'point_label') and proposal.point_label != '':
return (proposal.point_label)
else:
return None

def get_budget(self, proposal):
if hasattr(proposal, 'budget') and proposal.budget != 0:
return proposal.budget
else:
return None

def get_session_token_voted(self, proposal):
"""Serialize if proposal has been voted.

Expand Down
11 changes: 11 additions & 0 deletions meinberlin/assets/scss/components/_list_item.scss
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,20 @@
font-size: $font-size-sm;
}

.list-item__link {
font-size: $font-size-sm;
}

.list-item__labels {
@include clearfix;
margin: 0 0 0.4rem;

> span.label {
overflow: hidden;
max-width: 40ch;
text-overflow: ellipsis;
white-space: nowrap;
}
}

.list-item__label--moderator-feedback {
Expand Down
4 changes: 4 additions & 0 deletions meinberlin/assets/scss/utility.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
margin-left: $em-spacer;
}

.u-spacer-left-half {
margin-left: ($em-spacer * 0.5);
}

.u-spacer-right,
.a4-spacer--right {
margin-right: $em-spacer;
Expand Down