Skip to content

Commit

Permalink
Fix raw HTML visible on page load, fix broken links, improve readme (#…
Browse files Browse the repository at this point in the history
…495)

* Fix for raw HTML visible briefly on page load (#483)

* Update site copy (#223) (#224)

Copy changes, including cement deduction text

* reintroduce climate plans

* Add routing to climate plans

* updated calculations and data

* fix: hide climate plans before launch

* fixes raw html flicker

---------

* #421 contributing.md first draft (#489)

First version of a contribution guide.

* Ensure public assets served from the same domain are opened in a new tab (#490)

* Improve layout for 404 page

* Ensure public assets served from the same domain are opened in a new tab.

This helps us only preload Next.js data for internal links leading to actual pages.

* Use internal link to the same domain

* Some overviews to help see the big picture in Readme (#492)

* add en- and decoding to municipality routing (#493)

* add en- and decoding to municipality routing as well as minor cleanup

* Update __tests__/utils/generateMunipacitySitemap.test.ts

* Update __tests__/utils/generateMunipacitySitemap.test.ts

* Update __tests__/utils/generateMunipacitySitemap.test.ts

* Update pages/sitemap.xml.ts

* Update pages/sitemap.xml.ts

* Update utils/generateMunipacitySitemap.ts

---------

Co-authored-by: Samuel Plumppu <[email protected]>

* rm default arrow from toggle section plus minor cleanup (#494)

---------

Co-authored-by: Adebayo Ajayi <[email protected]>
Co-authored-by: Ludvig Janiuk <[email protected]>
Co-authored-by: Samuel Plumppu <[email protected]>
  • Loading branch information
4 people authored May 6, 2024
1 parent 3045adc commit 6186268
Show file tree
Hide file tree
Showing 13 changed files with 171 additions and 48 deletions.
39 changes: 33 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ That’s why we’re building a data-driven movement of climate-savvy developers

#Klimatkollen #FreeClimateData

## Climate Data Pipeline Overview

Please see full [description here](data/README.md).

Feel free to explore the repository to understand more about how we collect, process, and display climate data.

## Building and running locally

If your're starting from scratch, and working with GitHub, NodeJS and so on is new to you, read [doc/getting-started.md](doc/getting-started.md).
Expand All @@ -39,6 +33,37 @@ The project can also be run with docker (although with much slower refresh time)
# starts the container
docker run -t -i --rm -p 3000:3000 --name klimatkollen klimatkollen

# Data overview

In very general terms, Klimatkollen presents:
- Detailed information about Swedish municipalities' emissions...
- ...and their remaining emission budget based on the Paris Agreement.
- Other key point indicators for sustainability transition, such electric car charger density.
- Contextual information to help understand the significance of the above.

# File overview

The toplevel directory contains a lot of files and folders. You can just ignore most of them. Take note of:
- `README.md` - this document.
- `data`: Our data processing pipeline, written in Python. This can more or less be used/edited independently of the rest of the repository. See `data/README.md`.
- `data/facts`: Copies of source datasets.
- `doc`: Documentation and guides, they might answer many questions.
- `doc/getting-started.md`: Detailed setup instructions for the web project.
- `doc/contributing.md`: Good to know before making your first contribution.
- `pages` and `components`: Source code for almost everything visible on the website's pages.
- `public`: Files that will be served directly on the website.
- `public/locales`: Language files defininig translations of the website.

# Code architecture overview

How does everything fit together, code-wise?
- Copies of source datasets are under `data/facts`.
- We run the Python scripts under `data` to produce `data/output/climate-data.json` from those datasets.
- The latest copy of `data/output/climate-data.json` is always checked into version control.
- The rest of the website source code loads `data/output/climate-data.json`.
- The framework `next.js` is used to compile actual HTML pages at runtime.
- `next.js` caches each page for serving, to serve it faster for each new visitor.

## Contributing

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 All @@ -49,6 +74,8 @@ Looking for ideas on what needs to be done? We appreciate help on existing [issu

Testing, bug fixes, typos or fact checking of our data is highly appreciated.

See [doc/contributing.md] before making your first contribution.

## Contact

Join the Discord server in the introduction or send an email to [[email protected]](mailto:[email protected]).
Expand Down
6 changes: 3 additions & 3 deletions __tests__/utils/generateMunipacitySitemap.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TFunction } from 'next-i18next'
import {
generateMunipacitySitemapData,
generateMunicipalitySitemapData,
generateSitemap,
} from '../../utils/generateMunipacitySitemap'
import { Municipality } from '../../utils/types'
Expand All @@ -11,7 +11,7 @@ const t = vi.fn((str) => str) as unknown as TFunction

describe('generateSitemap', () => {
it('should generate valid municipality sitemap data', () => {
const siteMap = generateMunipacitySitemapData({ municipalities })
const siteMap = generateMunicipalitySitemapData({ municipalities })
expect(siteMap).toEqual([
{
url: 'https://klimatkollen.se/kommun/stockholm',
Expand All @@ -31,7 +31,7 @@ describe('generateSitemap', () => {
})

it('should generate a valid sitemap XML string', () => {
const siteMap = generateMunipacitySitemapData({ municipalities })
const siteMap = generateMunicipalitySitemapData({ municipalities })
const sitemapXml = generateSitemap(siteMap, t)
expect(() => new DOMParser().parseFromString(sitemapXml, 'text/xml')).not.toThrow()
})
Expand Down
2 changes: 1 addition & 1 deletion components/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const Main = styled.main`
}
`

export default function Layout({ children }: { children: JSX.Element }) {
export default function Layout({ children }: { children: JSX.Element | JSX.Element[] }) {
return (
<>
<Header />
Expand Down
16 changes: 15 additions & 1 deletion components/Markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import ReactMarkdown, { Components, Options } from 'react-markdown'
import rehypeExternalLinks from 'rehype-external-links'
import styled from 'styled-components'
import Link from 'next/link'
import { Ref } from 'react'

import {
H1, H2, H3, Paragraph, ParagraphBold, ParagraphItalic,
Expand All @@ -19,7 +20,20 @@ const defaultComponents: Partial<Components> = {
// Workaround to inherit styles and just change the HTML element
strong: styled(ParagraphBold).attrs({ as: 'strong' })``,
em: styled(ParagraphItalic).attrs({ as: 'em' })``,
a: Link as Components['a'],
// Ensure public assets served from the same domain (currently only PDFs) are opened in a new tab
// This prevents unwanted behavior where Next.js tries to preload data for URLs that are outside of the Next.js app.
a: ({
href, target, ref, rel, children,
}) => (
<Link
href={href as string}
target={href?.endsWith('.pdf') ? '_blank' : target}
rel={rel}
ref={ref as Ref<HTMLAnchorElement>}
>
{children}
</Link>
),
h1: H1,
h2: H2,
h3: H3,
Expand Down
18 changes: 6 additions & 12 deletions components/ToggleSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,21 @@ const Arrow = styled(ArrowSvg)<{ open: boolean }>`
const HeaderSection = styled.summary`
display: flex;
justify-content: space-between;
width: 100%;
list-style: none; /* remove default arrow in Firefox */
& .arrow {
display: block;
}
&::-webkit-details-marker {
display: none; /* remove default arrow in Chrome */
&:hover {
cursor: pointer;
}
}
`

const InfoSection = styled.div`
display: flex;
flex-direction: column;
.mobile {
background: black;
}
.desktop {
background: yellow;
}
`

type Props = {
Expand All @@ -60,7 +54,7 @@ function ToggleSection({ header, text }: Props) {
<TextSection onToggle={(event) => setOpen((event.target as HTMLDetailsElement).open)}>
<HeaderSection>
<H5>{header}</H5>
<Arrow open={open} className="arrow" />
<Arrow open={open} />
</HeaderSection>
<InfoSection>
{typeof text === 'string' ? <Markdown>{text}</Markdown> : text}
Expand Down
78 changes: 78 additions & 0 deletions doc/contributing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Klimatkollen contribution guidelines

Thank you for wanting to contribute to Klimatkollen! Please read this before starting to code :)

## What to expect from the team

Klimatkollen is driven by a small team that does a lot more than coding. From media outreach and fundraising to data analysis and hackathons, there's a lot going on. We sometimes have to push changes through in sync with exernal deadlines, for example to publish some data at the same time as a debate article. In such times we might not be able to review all proposed changes as soon as we would like to. As a rule of thumb we aspire to respond to every issue/PR within a week, and often much faster than that.

## How to get in touch

The best way to get in touch with us is through the discord (link in README). It's a very active server and all the discussions happen there, as well as many other day-to-day updates. You can also email at [email protected].

## Guide: How to make your first contribution

### Step 1. Find something to work on.

Either pick an issue from the GitHub issues tab, or create a new one to describe your suggestion/idea/bug report.

If you are creating a new issue, make sure to look for similar ones first. Use the search function!

Issues labelled "good first issue" are the best to start with if you are new to the project.

### Step 2. Let others know you're working on it.

Once you decide to pick an issue up, leave a comment saying so, to avoid others starting on the same one at the same time.

### Step 3. Make your changes

For most issues you will start with making sure you can run Klimatkollen locally, see [doc/getting-started.md] for instructions on that.

Run `git log -1` to see the latest commit in your local repo, and make sure this is the same as the `staging` branch on GitHub. This is to make sure you are up to date on everyone's changes.

(optional but recommended) Run `git checkout -b <issue-id>`, replacing `<issue-id>` with the id of the issue or another good name, to create a branch for you changes.

Make the code changes you would like to address the issue you are working on. It doesn't have to be perfect on first attempt, a prototype showing how an issue can be partially solved is already a step in the right direction.

Commit your work. You can make multiple commits if you like, and we don't enforce any particular style commit messages.

### Don't forget!

Your code contribution must not introduce new warnings or errors, or reduce the quality of the code. We will check this as part of review, but you can make the process faster by making sure we won't find anything.

Checklist before finishing your PR:

[ ] Run `npm run build` to compile all pages. Make sure there are no new warnings or errors.
[ ] Run `npm run lint` - same story.
[ ] Run `npm run start` to test the site locally and click through a few pages. Particularly focus on the places where you made your changes.

Also consider what it will be like for someone else to read your code. Will they be able to read it and understand what's going on?

### Step 4. Publish your work.

Push your commits to your fork of Klimatkollen. If you haven't created a fork already, use the "fork" button on GitHub and then add your fork as a remote by running `git remote add my-fork <git address of your fork>` where `my-fork` is an arbitrary name for the fork locally.

Create a pull request. Either follow the link git showed after pushing to your fork, or create it from the GitHub web interface. Double check the branches in the PR! Make sure your are merging from your own branch, on your own repo, into "staging" on "klimatkollen". A common mistake is to accidentally start merging into "staging" on your own repo.

In the PR desscription, write "Fixes #XXX" where XXX is the id of the issue you are fixing. E.g. `Fixes #413`

If the PR is not ready for review yet, prefix the title wiht "Draft: ". Remove this once you're ready.

In the body, describe the changes you have made, what you have done to test your work, if there's anything you're unsure about, or if some parts of the code are unfinished. Focus on the changes and how they address the issues - but try to keep discussion of the issue itself, in the issue.

### Step 5. Get your work reviewed.

Once the PR is created and has left the "draft" stage, someone from the team will eventually review it. This can normally take at least a few days - please be patient. At the same time, it is completely fine to let us know you're waiting for a review on Discord, and ask if you are unsure about anything.

The reviewer will most likely have some comments, questions, or feedback, which you will be expected to address.

In some cases, the reviewer might make some changes to your PR themselves and commit it, if they need to get the fix in quickly.

The reviewer might also conclude that the changes should not be made, or that there currently isn't time to review it. Such a scenario is unlikely but if it does happen, please be understanding and remember that the reviwer wants what's best for the project, just like you.

Klimatkollen's team has the final say on what goes into the code.

### Step 6. After the PR is merged

It might take some days before code merged to `staging` is published to production.

21 changes: 9 additions & 12 deletions pages/404.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,10 @@ import styled from 'styled-components'
import { useTranslation } from 'next-i18next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { GetStaticProps } from 'next'
import { H1 } from '../components/Typography'

const Wrapper = styled.div`
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
`
import { H1 } from '../components/Typography'
import Layout from '../components/Layout'
import PageWrapper from '../components/PageWrapper'

const Button = styled.button`
height: 56px;
Expand All @@ -35,10 +30,12 @@ function FourOhFour() {
const { t } = useTranslation()

return (
<Wrapper>
<H1>{t('common:errors.notFound')}</H1>
<Button onClick={handleClick}>{t('common:actions.goHome')}</Button>
</Wrapper>
<Layout>
<PageWrapper backgroundColor="black">
<H1>{t('common:errors.notFound')}</H1>
<Button onClick={handleClick}>{t('common:actions.goHome')}</Button>
</PageWrapper>
</Layout>
)
}

Expand Down
14 changes: 10 additions & 4 deletions pages/kommun/[municipality]/[step].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { ClimateDataService } from '../../../utils/climateDataService'
import { WikiDataService } from '../../../utils/wikiDataService'
import { Municipality as TMunicipality } from '../../../utils/types'
import { PolitycalRuleService as PoliticalRuleService } from '../../../utils/politicalRuleService'
import { PoliticalRuleService } from '../../../utils/politicalRuleService'

const Municipality = dynamic(() => import('../../../components/Municipality/Municipality'))

Expand Down Expand Up @@ -36,7 +36,9 @@ export default function Step({

const onNext = () => {
const next = CHARTS[stepIndex + 1]
if (!next) throw new Error(`Assertion failed: No step with index ${stepIndex + 1}`)
if (!next) {
throw new Error(`Assertion failed: No step with index ${stepIndex + 1}`)
}
router.replace(`/kommun/${id}/${next}`, undefined, {
shallow: true,
scroll: false,
Expand All @@ -45,7 +47,9 @@ export default function Step({

const onPrevious = () => {
const prev = CHARTS[stepIndex - 1]
if (!prev) throw new Error(`Assertion failed: No step with index ${stepIndex - 1}`)
if (!prev) {
throw new Error(`Assertion failed: No step with index ${stepIndex - 1}`)
}
router.replace(`/kommun/${id}/${prev}`, undefined, {
shallow: true,
scroll: false,
Expand Down Expand Up @@ -74,7 +78,9 @@ export const getServerSideProps: GetServerSideProps = async ({ params, res, loca
res.setHeader('Cache-Control', `public, stale-while-revalidate=60, max-age=${((60 * 60) * 24) * 7}`)

const id = (params as Params).municipality as string
if (cache.get(id)) return cache.get(id)
if (cache.get(id)) {
return cache.get(id)
}

const climateDataService = new ClimateDataService()
const wikiDataService = new WikiDataService()
Expand Down
13 changes: 10 additions & 3 deletions pages/kommun/[municipality]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@ import { useEffect } from 'react'
import { ParsedUrlQuery } from 'querystring'
import { CHARTS } from './[step]'

const municipalityRoute = (name: string) => `/kommun/${encodeURIComponent(name)}/${CHARTS[1]}`

export default function Index() {
const router = useRouter()
const municipality = router.query.municipality as string

useEffect(() => {
if (municipality) router.replace(`/kommun/${municipality}/${CHARTS[0]}`)
if (municipality) {
const decodedName = decodeURIComponent(municipality)
router.replace(municipalityRoute(decodedName))
}
}, [municipality, router])

return ''
return null
}

interface Params extends ParsedUrlQuery {
Expand All @@ -22,9 +27,11 @@ interface Params extends ParsedUrlQuery {
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
const id = (params as Params).municipality as string

const decodedId = decodeURIComponent(id)

return {
redirect: {
destination: `/kommun/${id}/framtida-prognos`,
destination: municipalityRoute(decodedId),
permanent: true,
},
}
Expand Down
4 changes: 2 additions & 2 deletions pages/sitemap.xml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { GetServerSideProps } from 'next'

import { ClimateDataService } from '../utils/climateDataService'
import {
generateMunipacitySitemapData,
generateMunicipalitySitemapData,
generateSitemap,
} from '../utils/generateMunipacitySitemap'
import { getServerSideI18n } from '../utils/getServerSideI18n'
Expand All @@ -11,7 +11,7 @@ export const getServerSideProps: GetServerSideProps = async ({ res, locale }) =>
const { t } = await getServerSideI18n(locale as string, ['common', 'sitemap'])

const municipalities = new ClimateDataService().getMunicipalities()
const municipalitiesSitemap = generateMunipacitySitemapData({ municipalities })
const municipalitiesSitemap = generateMunicipalitySitemapData({ municipalities })
// Generate the XML sitemap with the blog data
const sitemap = generateSitemap(municipalitiesSitemap, t)

Expand Down
2 changes: 1 addition & 1 deletion public/locales/sv/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"title": "Vår styrelse"
},
"content": {
"text": "I dag kan du på Klimatkollen se:\n\n- Koldioxidbudgetar för landets alla 290 kommuner. Klimatkollen utgår ifrån en [nationell koldioxidbudget](https://www.cemus.uu.se/wp-content/uploads/2023/12/Paris-compliant-carbon-budgets-for-Swedens-counties-.pdf) som beräknats av forskare vid Uppsala universitet enligt Tyndall-modellen och som sedan fördelats ut på kommunerna av Klimatkollen. (2022 koldioxidbudget [här](https://klimatkollen.se/Paris_compliant_Swedish_CO2_budgets-March_2022-Stoddard&Anderson.pdf))\n- En översikt i både kartvy och listvy över koldioxidutsläppen i landets kommuner. Kommunerna rankas baserat på genomsnittlig årlig utsläppsminskningstakt sedan Parisavtalet undertecknades 2015.\n- En visualisering av hur det gått med koldioxidutsläppen för varje kommun från 1990 tills idag, hur det borde gå för att klara Parisavtalets 1,5-gradersmål och en prognos för hur det går om utsläppen i kommunen fortsätter att utvecklas som de senaste fem åren.\n- För varje kommun finns även annan nyckelfakta, som koldioxidbudget, datum när budgeten tar slut med nuvarande utsläppstakt, politiskt styre och koldioxidutsläpp per invånare.\n\nKlimatkollen är utvecklad med öppen källkod. Det betyder att alla kan vara med och utveckla och förbättra sajten via vårt [Github-repo](https://github.com/Klimatbyran/klimatkollen).",
"text": "I dag kan du på Klimatkollen se:\n\n- Koldioxidbudgetar för landets alla 290 kommuner. Klimatkollen utgår ifrån en [nationell koldioxidbudget](https://www.cemus.uu.se/wp-content/uploads/2023/12/Paris-compliant-carbon-budgets-for-Swedens-counties-.pdf) som beräknats av forskare vid Uppsala universitet enligt Tyndall-modellen och som sedan fördelats ut på kommunerna av Klimatkollen. (2022 koldioxidbudget [här](/Paris_compliant_Swedish_CO2_budgets-March_2022-Stoddard&Anderson.pdf))\n- En översikt i både kartvy och listvy över koldioxidutsläppen i landets kommuner. Kommunerna rankas baserat på genomsnittlig årlig utsläppsminskningstakt sedan Parisavtalet undertecknades 2015.\n- En visualisering av hur det gått med koldioxidutsläppen för varje kommun från 1990 tills idag, hur det borde gå för att klara Parisavtalets 1,5-gradersmål och en prognos för hur det går om utsläppen i kommunen fortsätter att utvecklas som de senaste fem åren.\n- För varje kommun finns även annan nyckelfakta, som koldioxidbudget, datum när budgeten tar slut med nuvarande utsläppstakt, politiskt styre och koldioxidutsläpp per invånare.\n\nKlimatkollen är utvecklad med öppen källkod. Det betyder att alla kan vara med och utveckla och förbättra sajten via vårt [Github-repo](https://github.com/Klimatbyran/klimatkollen).",
"title": "Vad finns på Klimatkollen?"
},
"earlierProjects": {
Expand Down
Loading

0 comments on commit 6186268

Please sign in to comment.