Skip to content

Commit

Permalink
Feature/basic i18n (#452)
Browse files Browse the repository at this point in the history
* Init i18next

* Remove unused file

* Switch to next-i18next

* Add configuration and translate cookie banner

* Fix Next.js warning "API handler should not return a value, received object"

* Translate footer and add eslint rule to avoid import mistake

* Ensure SSR and CSR bundles provide consistent i18n config. See i18next/next-i18next#2259 for details

* Remove unused page

* Remove unused property

* Add top-level translations for StartPage

* No need for translations when redirecting

* Add 404 translations

* Simplify DropDown to remove unused classes

* Fix formatting error in Header

* Remove Footer tests since they 1) it wasn't worth adapting them to the new i18n structure, and 2) because they tested the content rather than cricital functionality

* Combine footer and common translations into a single namespace

* Fix formatting issue

* Add basic i18n to remaining pages

* Use markdown to simplify i18n for dataset definitions

* Style Markdown content according to the theme, with the option to override components

* Use markdown for `body` field of DataDescriptions

* Simplify date formatting

* Prevent styled-components from rendering invalid HTML props

* Remove unused components

* Add translations for common components

* Fix layout issue

* Begin adding municipality translations

* Add translations for municipality numbers

* Allow ScorecardSections to use consistent formattig even with markdown content

* Add municipality ScoreCard translations

* Use HTML details element to make ScorecardSection more accessible and easier to use

* Styling tweaks

* Add missing key attribute

* Fix React hydration error caused by missing <thead> element

* Add solutions translations

* Improve styles for Markdown

* Replace JSX with Markdown formatting

* Use markdown formatting instead of JS override

* Translate utsläppsberakningar and use Markdown for simplified formatting

* Translate partierna, and update next/image props

* Translate Källor och Metod

* Improve grid styling on about page

* Translate about page

* Improve spacing

* Clarify dataset task ideas

* Translate sitemap

* Update tests to use mocked i18n and test against expected keys rather than values

* Begin translating datasets

* Translate the easy parts of dataset definitions

* Clarify naming

* Replace deprecated method String#substr with String#slice

* Refactor to prepare for translated dataDescriptions

* Add translations for datasets

* WIP: hack around broken SSR translations

* Cleanup SSR translations

* Separate hardcoded dataset keys from the UI labels, to allow translations

* Update tests to reflect fully translated datasets

* Reduce margin-top for FactSection

* Fix translation error

* Fit images in container width

* Fix modal positioning. Also improve UX by closing on backdrop click. Long term our custom Modal should be replaced with a standard Modal solution.

* Ensure special data for climate plans and procurements renders correctly. Also improve TypeScript types to prevent this error in the future.

* Fix type error

* Revert spacing change for CementClarification
  • Loading branch information
Greenheart committed Apr 23, 2024
1 parent 2e6fef2 commit e85f934
Show file tree
Hide file tree
Showing 63 changed files with 2,696 additions and 2,658 deletions.
6 changes: 6 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,11 @@ module.exports = {
'import/no-extraneous-dependencies': 'off', // fixme revisit and refactor files when there's time to spare
'react/react-in-jsx-scope': 'off',
'react/jsx-one-expression-per-line': 'warn',
'no-restricted-imports': ['error', {
paths: [{
name: 'react-i18next',
message: 'Please import useTranslation from next-i18next.',
}],
}],
},
}
57 changes: 0 additions & 57 deletions __tests__/components/Footer/Footer.test.tsx

This file was deleted.

38 changes: 0 additions & 38 deletions __tests__/components/Footer/FooterNewsletterForm.test.tsx

This file was deleted.

22 changes: 0 additions & 22 deletions __tests__/components/Footer/FooterNewsletterSubscribe.test.tsx

This file was deleted.

31 changes: 0 additions & 31 deletions __tests__/components/Footer/FooterPartners.test.tsx

This file was deleted.

37 changes: 0 additions & 37 deletions __tests__/components/Footer/FooterSocialLinks.test.tsx

This file was deleted.

19 changes: 13 additions & 6 deletions __tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { fireEvent, render, screen } from '@testing-library/react'
import '@testing-library/jest-dom'
import Home from '../pages/index'

vi.mock('../public/icons/info.svg', () => ({ default: () => 'svg' }))
Expand All @@ -22,6 +21,12 @@ vi.mock('next/router', () => ({
}),
}))

vi.mock('next-i18next', () => ({
useTranslation: vi.fn(() => ({
t: (str: string) => str,
})),
}))

describe('Home Page', () => {
// Mock data for municipalities
const mockMunicipalities = [
Expand Down Expand Up @@ -65,6 +70,8 @@ describe('Home Page', () => {
TotalConsumptionEmission: 0,
ElectricVehiclePerChargePoints: 0,
Name: 'Sollentuna',
ProcurementScore: 0,
ProcurementLink: '',
},
]

Expand All @@ -73,24 +80,24 @@ describe('Home Page', () => {
})

it('renders without crashing', () => {
expect(screen.getByText(/Hur går det med?/)).toBeInTheDocument()
expect(screen.getByText(/startPage:questionTitle/)).toBeInTheDocument()
})

it('changes view mode when toggle button is clicked', () => {
const toggleButton = screen.getByText('Listvy')
const toggleButton = screen.getByText('startPage:toggleView.list')
fireEvent.click(toggleButton)
expect(screen.getByText('Kartvy')).toBeInTheDocument()
expect(screen.getByText('startPage:toggleView.map')).toBeInTheDocument()
})

it('handles dataset change', () => {
const newDataset = 'Klimatplanerna'
const newDataset = 'common:datasets.plans.name'
const radioButton = screen.getByLabelText(newDataset)
fireEvent.click(radioButton)
expect(screen.getByText(newDataset)).toBeInTheDocument()
})

it('renders the dropdown component', () => {
const dropdownInput = screen.getByPlaceholderText(/hur går det i din kommun?/i)
const dropdownInput = screen.getByPlaceholderText(/startPage:yourMunicipality/i)
expect(dropdownInput).toBeInTheDocument()
})
})
6 changes: 5 additions & 1 deletion __tests__/utils/generateMunipacitySitemap.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { TFunction } from 'next-i18next'
import {
generateMunipacitySitemapData,
generateSitemap,
} from '../../utils/generateMunipacitySitemap'
import { Municipality } from '../../utils/types'

const municipalities = [{ Name: 'Stockholm' }, { Name: 'Göteborg' }] as Municipality[]

const t = vi.fn((str) => str) as unknown as TFunction

describe('generateSitemap', () => {
it('should generate valid municipality sitemap data', () => {
const siteMap = generateMunipacitySitemapData({ municipalities })
Expand All @@ -28,7 +32,7 @@ describe('generateSitemap', () => {

it('should generate a valid sitemap XML string', () => {
const siteMap = generateMunipacitySitemapData({ municipalities })
const sitemapXml = generateSitemap(siteMap)
const sitemapXml = generateSitemap(siteMap, t)
expect(() => new DOMParser().parseFromString(sitemapXml, 'text/xml')).not.toThrow()
})
})
12 changes: 7 additions & 5 deletions components/ComparisonTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,13 @@ function ComparisonTable<T extends object>({ data, columns }: TableProps<T>) {

return (
<StyledTable key={resizeCount}>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header, index) => renderHeader(header, index))}
</tr>
))}
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header, index) => renderHeader(header, index))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<TableRow key={row.id} onClick={() => handleRowClick(row)}>
Expand Down
12 changes: 7 additions & 5 deletions components/DropDown.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useState, useRef, useEffect } from 'react'
import styled from 'styled-components'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'

import ArrowDown from '../public/icons/arrow-down.svg'
import { devices } from '../utils/devices'

Expand Down Expand Up @@ -83,7 +85,6 @@ const ErrorText = styled.div`
type Props = {
municipalitiesName: Array<string>
placeholder: string
className: string
}

export function getSortedMunicipalities(municipalitiesName: Array<string>) {
Expand Down Expand Up @@ -113,7 +114,7 @@ export function search(query: string, municipalitiesName: Array<string>) {
})
}

function DropDown({ municipalitiesName, placeholder, className }: Props) {
function DropDown({ municipalitiesName, placeholder }: Props) {
const sortedMunicipalities = getSortedMunicipalities(municipalitiesName)
const [showDropDown, setShowDropDown] = useState(false)
const [selectedMunicipality, setSelectedMunicipality] = useState<string>('')
Expand All @@ -122,6 +123,7 @@ function DropDown({ municipalitiesName, placeholder, className }: Props) {

const ref = useRef<HTMLDivElement>(null)
const router = useRouter()
const { t } = useTranslation()

useEffect(() => {
const checkIfClickedOutside = (e: MouseEvent) => {
Expand Down Expand Up @@ -192,13 +194,13 @@ function DropDown({ municipalitiesName, placeholder, className }: Props) {
value={selectedMunicipality}
/>
<Btn onClick={() => setShowDropDown((current) => !current)}>
<ArrowDown aria-label="Visa kommun" />
<ArrowDown aria-label={t('common:components.DropDown.label')} />
</Btn>
</Flex>
{showDropDown && (
<MunicipalitiesWrapper className={className}>
<MunicipalitiesWrapper>
{municipalities.map((name) => (
<Municipality onClick={() => onMunicipalityClick(name)}>
<Municipality onClick={() => onMunicipalityClick(name)} key={name}>
{name}
</Municipality>
))}
Expand Down
9 changes: 7 additions & 2 deletions components/FactSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useState } from 'react'
import { H3, ParagraphBold } from './Typography'
import Icon from '../public/icons/add_light_white.svg'
import IconGreen from '../public/icons/remove_light_white.svg'
import Markdown from './Markdown'

const Row = styled.summary`
display: flex;
Expand Down Expand Up @@ -40,6 +41,10 @@ const InfoSection = styled.div`
text-decoration: underline;
cursor: pointer;
}
& p:first-of-type {
margin-top: 0;
}
`

const StyledIcon = styled.div`
Expand All @@ -53,7 +58,7 @@ const StyledIcon = styled.div`
type Props = {
heading: string
data: string
info?: JSX.Element | string
info?: string
}

function FactSection({ heading, data, info }: Props) {
Expand All @@ -75,7 +80,7 @@ function FactSection({ heading, data, info }: Props) {
)}
</Row>
<InfoSection>
{info}
<Markdown>{info}</Markdown>
</InfoSection>
</details>
)
Expand Down
Loading

0 comments on commit e85f934

Please sign in to comment.