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

Improved drop-down search, add docker build, improved UI, fixed typos and cleanup #406

Merged
merged 10 commits into from
Mar 8, 2024
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.git
*Dockerfile*
*docker-compose*
node_modules
15 changes: 15 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM node:21-alpine3.18

WORKDIR /app

COPY package.json ./

RUN npm install

COPY . ./

EXPOSE 3000

ENTRYPOINT [ "npm" ]

CMD [ "run", "dev" ]
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ We use next.js and Typescript and it's pretty straightforward to get started. Ju

This opens up a webserver on http://localhost:3000. Just edit the code and see the live refresh.

The project can also be run with docker (although with much slower refresh time):

# builds the image
docker build -t klimatkollen .

# starts the container
docker run -t -i --rm -p 3000:3000 --name klimatkollen klimatkollen

## Contribute

The idea behind Klimatkollen is to give citizens access to the climate data we need to meet the goals of the Paris Agreement – and save our own future.
Expand Down
30 changes: 30 additions & 0 deletions __tests__/components/DropDown.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { test } from 'vitest'
import { getSortedMunicipalities, search } from '../../components/DropDown'

test('Municipalities should be sorted by Swedish alphabetical order', () => {
expect(
getSortedMunicipalities(['Örebro', 'Säffle', 'Älmhult', 'Åre', 'Karlshamn']),
).toEqual(['Karlshamn', 'Säffle', 'Åre', 'Älmhult', 'Örebro'])
})

describe('When searching a list of sorted municipalities', () => {
const municipalities = ['Göteborg', 'Malmö', 'Stockholm', 'Södertälje']

test('Results that do not include the query should be filtered out', () => {
expect(search('holm', municipalities)).toEqual(['Stockholm'])
expect(search('s', municipalities)).toEqual(['Stockholm', 'Södertälje'])
})

test('The search is case-insensitive', () => {
expect(search('göt', municipalities)).toEqual(['Göteborg'])
expect(search('GÖt', municipalities)).toEqual(['Göteborg'])
})

test('Results that start with the query should be prioritized', () => {
expect(search('st', ['Avesta', 'Mariestad', 'Stockholm'])).toEqual([
'Stockholm',
'Avesta',
'Mariestad',
])
})
})
3 changes: 2 additions & 1 deletion components/BackArrow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ const StyledButton = styled.button`
border: none;
cursor: pointer;
background-color: transparent;
align-self: flex-start;
width: 60px;
text-align: left;
`

type BackArrowProps = {
Expand Down
31 changes: 29 additions & 2 deletions components/DropDown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,35 @@ type Props = {
className: string
}

export function getSortedMunicipalities(municipalitiesName: Array<string>) {
return municipalitiesName.sort((a, b) => a.localeCompare(b, 'sv'))
}

export function search(query: string, municipalitiesName: Array<string>) {
const queryLowerCase = query.toLowerCase()

return municipalitiesName
.filter((municipality) => municipality.toLowerCase().includes(queryLowerCase))
.sort((a, b) => {
const lowerA = a.toLowerCase()
const lowerB = b.toLowerCase()

const startsWithQueryA = lowerA.startsWith(queryLowerCase)
const startsWithQueryB = lowerB.startsWith(queryLowerCase)

if (startsWithQueryA && !startsWithQueryB) {
return -1
}
if (!startsWithQueryA && startsWithQueryB) {
return 1
}

return 0
})
}

function DropDown({ municipalitiesName, placeholder, className }: Props) {
const sortedMunicipalities = municipalitiesName.sort((a, b) => a.localeCompare(b))
const sortedMunicipalities = getSortedMunicipalities(municipalitiesName)
const [showDropDown, setShowDropDown] = useState(false)
const [selectedMunicipality, setSelectedMunicipality] = useState<string>('')
const [municipalities, setMunicipalities] = useState(sortedMunicipalities)
Expand Down Expand Up @@ -128,7 +155,7 @@ function DropDown({ municipalitiesName, placeholder, className }: Props) {
} else {
setShowDropDown(true)
}
const filteredMunicipalities = sortedMunicipalities.filter((test) => test.toLowerCase().includes(value.toLowerCase()))
const filteredMunicipalities = search(value, sortedMunicipalities)
setMunicipalities(filteredMunicipalities)
}

Expand Down
6 changes: 3 additions & 3 deletions components/Graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,13 @@ function Graph({
// @ts-ignore
id: 'budget',
fill: true,
data: budgetDataset,
borderWidth: 2,
data: step >= 2 ? budgetDataset : budgetDataset.map(({ x }) => ({ x, y: 0 })),
borderWidth: step >= 2 ? 2 : 0,
borderColor: colorTheme.lightGreen,
backgroundColor: colorTheme.lightGreenOpaqe,
pointRadius: 0,
tension: 0.15,
hidden: step < 2,
hidden: false,
},
{
// @ts-ignore
Expand Down
11 changes: 6 additions & 5 deletions components/Municipality/Municipality.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import styled from 'styled-components'

import { H1, ParagraphBold } from '../Typography'
import { H1NoPad, ParagraphBold } from '../Typography'
import BackArrow from '../BackArrow'
import PageWrapper from '../PageWrapper'
import DropDown from '../DropDown'
Expand All @@ -16,7 +16,8 @@ import Scorecard from './MunicipalityScorecard'
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
gap: 2rem;
align-items center;
gap: 1.5rem;
margin-bottom: 48px;
`

Expand All @@ -29,7 +30,7 @@ const HeaderSection = styled.div`
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-top: 20px;
margin-top: 10px;
`

const Bottom = styled.div`
Expand Down Expand Up @@ -74,10 +75,10 @@ function Municipality(props: Props) {
return (
<>
<PageWrapper backgroundColor="lightBlack">
<BackArrow route="/" />
<StyledContainer>
<HeaderSection>
<H1>{municipality.Name}</H1>
<BackArrow route="/" />
<H1NoPad>{municipality.Name}</H1NoPad>
{coatOfArmsImage && (
<CoatOfArmsImage
src={coatOfArmsImage}
Expand Down
18 changes: 18 additions & 0 deletions components/Typography.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,31 @@ export const H1 = styled.h1`
font-size: 48px;
line-height: 1.25;
margin: 0 0 16px 0;

@media (max-width: 768px) {
font-size: 42px;
}
`

export const H1NoPad = styled.h1`
font-weight: bold;
font-size: 48px;
line-height: 1.25;

@media (max-width: 768px) {
font-size: 35px;
}
`

export const H2 = styled.h2`
font-weight: bold;
font-size: 32px;
line-height: 1.25;
margin: 0 0 8px 0;

@media (max-width: 768px) {
font-size: 24px;
}
`

export const H2Regular = styled.h2`
Expand Down
2 changes: 1 addition & 1 deletion pages/om-oss/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ function OmOss() {
</Paragraph>
<Paragraph>
Våra samarbetspartners under de första två åren har varit Världsnaturfonden WWF,
Postkodstiftelsen, PwC, ClimateView,, Klimatklubben, Våra barns klimat,
Postkodstiftelsen, PwC, ClimateView, Klimatklubben, Våra barns klimat,
Researchers’ Desk, Argand Partners och We Don’t Have Time.
</Paragraph>
</>
Expand Down
94 changes: 47 additions & 47 deletions utils/datasetDescriptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,6 @@ export const datasetDescriptions: DatasetDescriptions = {
formattedDataPoint: (dataPoint) => ((dataPoint as number) * 100).toFixed(1),
},

Elbilarna: {
title: 'Elbilsökning',
body: 'Ökningstakten i kommunerna för andel nyregistrerade laddbara bilar 2015–2022, angivet i procentenheter per år.',
source: (
<>
Källa:
{' '}
<a href="https://www.trafa.se/vagtrafik/fordon/" target="_blank" rel="noreferrer">
Trafikanalys
</a>
</>
),
boundaries: [0.04, 0.05, 0.06, 0.07, 0.08],
labels: ['4 -', '4–5', '5–6', '6–7', '7–8', '8 +'],
labelRotateUp: [true, true, true, true, true, true],
columnHeader: 'Ökning elbilar',
sortAscending: false,
rawDataPoint: (item) => item.ElectricCarChangePercent,
formattedDataPoint: (dataPoint) => ((dataPoint as number) * 100).toFixed(1),
},

Klimatplanerna: {
title: 'Klimatplan',
body: (
Expand Down Expand Up @@ -106,6 +85,53 @@ export const datasetDescriptions: DatasetDescriptions = {
formattedDataPoint: (dataPoint) => (dataPoint === 'Saknas' ? 'Nej' : 'Ja'),
},

Elbilarna: {
title: 'Elbilsökning',
body: 'Ökningstakten i kommunerna för andel nyregistrerade laddbara bilar 2015–2022, angivet i procentenheter per år.',
source: (
<>
Källa:
{' '}
<a href="https://www.trafa.se/vagtrafik/fordon/" target="_blank" rel="noreferrer">
Trafikanalys
</a>
</>
),
boundaries: [0.04, 0.05, 0.06, 0.07, 0.08],
labels: ['4 -', '4–5', '5–6', '6–7', '7–8', '8 +'],
labelRotateUp: [true, true, true, true, true, true],
columnHeader: 'Ökning elbilar',
sortAscending: false,
rawDataPoint: (item) => item.ElectricCarChangePercent,
formattedDataPoint: (dataPoint) => ((dataPoint as number) * 100).toFixed(1),
},

Laddarna: {
title: 'Elbilar per laddare',
body: 'Antal laddbara bilar per offentliga laddpunkter år 2023. EU rekommenderar max 10 bilar per laddare.',
source: (
<>
Källa:
{' '}
<a
href="https://powercircle.org/elbilsstatistik/"
target="_blank"
rel="noreferrer"
>
Power Circle ELIS
</a>
{' '}
</>
),
boundaries: [1e6, 40, 30, 20, 10],
labels: ['Inga laddare', '40 +', '30-40', '20-30', '10-20', '10 -'],
labelRotateUp: [],
columnHeader: 'Elbil per laddare',
sortAscending: true,
rawDataPoint: (item) => item.ElectricVehiclePerChargePoints,
formattedDataPoint: (dataPoint) => ((dataPoint as number) < 1e5 ? (dataPoint as number).toFixed(1) : 'Laddare saknas'),
},

Cyklarna: {
title: 'Cykelvägslängd',
body: 'Antal meter cykelväg per invånare per kommun år 2022.',
Expand Down Expand Up @@ -173,32 +199,6 @@ export const datasetDescriptions: DatasetDescriptions = {
formattedDataPoint: (dataPoint) => (dataPoint as number).toFixed(1),
},

Laddarna: {
title: 'Elbilar per laddare',
body: 'Antal laddbara bilar per offentliga laddpunkter år 2023. EU rekommenderar max 10 bilar per laddare.',
source: (
<>
Källa:
{' '}
<a
href="https://powercircle.org/elbilsstatistik/"
target="_blank"
rel="noreferrer"
>
Power Circle ELIS
</a>
{' '}
</>
),
boundaries: [1e6, 40, 30, 20, 10],
labels: ['Inga laddare', '40 +', '30-40', '20-30', '10-20', '10 -'],
labelRotateUp: [],
columnHeader: 'Elbil per laddare',
sortAscending: true,
rawDataPoint: (item) => item.ElectricVehiclePerChargePoints,
formattedDataPoint: (dataPoint) => ((dataPoint as number) < 1e5 ? (dataPoint as number).toFixed(1) : 'Laddare saknas'),
},

Koldioxidbudgetarna: {
title: 'Budget slut om',
body: 'Datum då kommunens koldioxidbudget tar slut om utsläppen fortsätter enligt nuvarande trend. Några kommuner kommer att hålla budgeten om trenden står sig.',
Expand Down
Loading