refactor(site): use typescript (#225)
This commit is contained in:
parent
a55ac31389
commit
9d8b6ddd18
|
@ -15,9 +15,8 @@ import { H1 } from './Typography'
|
|||
import { useIntl } from 'react-intl'
|
||||
|
||||
const Banner = () => {
|
||||
|
||||
const intl = useIntl()
|
||||
|
||||
|
||||
return (
|
||||
<div className="header">
|
||||
<div className="relative max-w-6xl mx-auto mt-5 mb-20 md:pt-32 md:mb-52">
|
||||
|
@ -62,10 +61,8 @@ const Banner = () => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<H1>{intl.formatMessage({ id: 'general.title'})}</H1>
|
||||
<p>
|
||||
{intl.formatMessage({ id: 'general.description'})}
|
||||
</p>
|
||||
<H1>{intl.formatMessage({ id: 'general.title' })}</H1>
|
||||
<p>{intl.formatMessage({ id: 'general.description' })}</p>
|
||||
<p className="py-4 flex items-center sm:flex-row space-x-2 md:space-x-4">
|
||||
<Link.External
|
||||
className="inline-block"
|
||||
|
@ -90,7 +87,7 @@ const Banner = () => {
|
|||
/>
|
||||
</Link.External>
|
||||
</p>
|
||||
<p className="flex flex-col sm:items-center mt-5 sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2">
|
||||
<p className="flex flex-col mt-5 sm:items-center sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2">
|
||||
<NextLink href="/integritet">
|
||||
<a className="inline-block px-4 py-2 font-bold text-indigo-800 border-2 border-indigo-800 rounded-full md:px-8 md:py-4 hover:bg-indigo-800 hover:text-white">
|
||||
Integritetspolicy
|
|
@ -1,6 +1,13 @@
|
|||
import Link from 'next/link'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
const ButtonLink = ({ children, href, target }) => {
|
||||
interface ButtonLinkProps {
|
||||
children: ReactNode
|
||||
href: string
|
||||
target?: string
|
||||
}
|
||||
|
||||
const ButtonLink = ({ children, href, target }: ButtonLinkProps) => {
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
|
@ -13,7 +20,16 @@ const ButtonLink = ({ children, href, target }) => {
|
|||
)
|
||||
}
|
||||
|
||||
export const ButtonLinkInternal = ({ children, href }) => {
|
||||
interface ButtonLinkInternalProps {
|
||||
children: ReactNode
|
||||
href: string
|
||||
}
|
||||
|
||||
export const ButtonLinkInternal = ({
|
||||
children,
|
||||
href,
|
||||
}: ButtonLinkInternalProps) => {
|
||||
console.log(href)
|
||||
return (
|
||||
<Link href={href}>
|
||||
<a className="inline-block px-4 py-2 font-bold text-indigo-800 border-2 border-indigo-800 rounded-full cursor-pointer md:px-8 md:py-4 hover:bg-indigo-800 hover:text-white">
|
|
@ -1,4 +1,4 @@
|
|||
import SwiperCore, { Autoplay, Pagination } from 'swiper'
|
||||
import SwiperCore, { Autoplay, Pagination, SwiperOptions } from 'swiper'
|
||||
import { Swiper, SwiperSlide } from 'swiper/react'
|
||||
import img4 from '../assets/img/icons/goal.svg'
|
||||
import img3 from '../assets/img/icons/planning.svg'
|
||||
|
@ -42,7 +42,7 @@ const FEATURES_DATA = [
|
|||
]
|
||||
|
||||
const Features = () => {
|
||||
const swiperParams = {
|
||||
const swiperParams: SwiperOptions = {
|
||||
slidesPerView: 3,
|
||||
slidesPerGroup: 3,
|
||||
centeredSlides: true,
|
|
@ -26,7 +26,7 @@ const FunFacts = () => {
|
|||
startCounter: false,
|
||||
})
|
||||
|
||||
const onVisibilityChange = (isVisible) => {
|
||||
const onVisibilityChange = (isVisible: boolean) => {
|
||||
if (isVisible) {
|
||||
setCounter({ startCounter: true })
|
||||
}
|
|
@ -5,7 +5,9 @@ import NavLinks from './NavLinks'
|
|||
import classnames from 'classnames'
|
||||
import Icon from './Icon'
|
||||
|
||||
const useMobile = (cb) => {
|
||||
type UseMobileCallback = (ev: MediaQueryListEvent) => any
|
||||
|
||||
const useMobile = (cb: UseMobileCallback) => {
|
||||
React.useEffect(() => {
|
||||
const mobileMql = window.matchMedia('(max-width: 480px)')
|
||||
|
||||
|
@ -20,7 +22,7 @@ const useMobile = (cb) => {
|
|||
mobileMql.removeEventListener?.('change', cb)
|
||||
|
||||
if (mobileMql.removeListener) {
|
||||
mobileMql.removeListener('change', cb)
|
||||
mobileMql.removeListener(cb)
|
||||
}
|
||||
}
|
||||
}, [])
|
|
@ -1,10 +1,15 @@
|
|||
import Head from 'next/head'
|
||||
import React from 'react'
|
||||
import { ReactNode } from 'react'
|
||||
import favImg from '../assets/img/favicon.png'
|
||||
import logo from '../assets/img/logo.png'
|
||||
import { GA_TRACKING_ID } from './gtag'
|
||||
|
||||
const Layout = (props) => {
|
||||
interface LayoutProps {
|
||||
children: ReactNode
|
||||
pageTitle: string
|
||||
}
|
||||
|
||||
const Layout = ({ children, pageTitle }: LayoutProps) => {
|
||||
return (
|
||||
<div>
|
||||
<Head>
|
||||
|
@ -19,7 +24,7 @@ const Layout = (props) => {
|
|||
content="Öppna Skolplattformen är en app för iOS och Android som gör det enklare för föräldrar att komma åt uppgifter i Skolplattformen."
|
||||
/>
|
||||
<meta property="og:image" content={logo} />
|
||||
<title>{props.pageTitle}</title>
|
||||
<title>{pageTitle}</title>
|
||||
<link rel="shortcut icon" type="image/png" href={favImg} />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||
<link
|
||||
|
@ -44,7 +49,7 @@ const Layout = (props) => {
|
|||
/>
|
||||
</Head>
|
||||
<div className="page-wrapper" id="wrapper">
|
||||
{props.children}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
|
@ -1,7 +1,13 @@
|
|||
import Link from 'next/link'
|
||||
import classnames from 'classnames'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
const Internal = ({ href, children }) => {
|
||||
interface LinkInternalProps {
|
||||
children: ReactNode
|
||||
href: string
|
||||
}
|
||||
|
||||
const Internal = ({ href, children }: LinkInternalProps) => {
|
||||
return (
|
||||
<Link href={href}>
|
||||
<a className="text-indigo-800">{children}</a>
|
||||
|
@ -9,7 +15,19 @@ const Internal = ({ href, children }) => {
|
|||
)
|
||||
}
|
||||
|
||||
const External = ({ className, href, children, target = '_blank' }) => {
|
||||
interface LinkExternalProps {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
href: string
|
||||
target?: string
|
||||
}
|
||||
|
||||
const External = ({
|
||||
className,
|
||||
href,
|
||||
children,
|
||||
target = '_blank',
|
||||
}: LinkExternalProps) => {
|
||||
return (
|
||||
<a
|
||||
className={classnames('text-indigo-800', className)}
|
|
@ -4,11 +4,15 @@ import { Link as ScrollLink } from 'react-scroll'
|
|||
import { pageview } from './gtag'
|
||||
import { useIntl } from 'react-intl'
|
||||
|
||||
const NavLinks = ({ onClick }) => {
|
||||
const { pathname } = useRouter()
|
||||
const intl = useIntl();
|
||||
interface NavLinksProps {
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
const path = (href) => {
|
||||
const NavLinks = ({ onClick }: NavLinksProps) => {
|
||||
const { pathname } = useRouter()
|
||||
const intl = useIntl()
|
||||
|
||||
const path = (href: string) => {
|
||||
const hashIndex = href.indexOf('#')
|
||||
if (hashIndex < 0) return href
|
||||
return href.substring(0, hashIndex)
|
||||
|
@ -40,7 +44,7 @@ const NavLinks = ({ onClick }) => {
|
|||
<ul className="flex flex-col text-xl text-gray-800 md:flex-row md:items-center space-y-4 md:space-y-0 md:space-x-8 md:text-base">
|
||||
<li>
|
||||
<Link to="wrapper" href="/#">
|
||||
{intl.formatMessage({ id: 'navigation.home'})}
|
||||
{intl.formatMessage({ id: 'navigation.home' })}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -48,17 +52,17 @@ const NavLinks = ({ onClick }) => {
|
|||
</li>
|
||||
<li>
|
||||
<Link to="funktioner" href="/#funktioner">
|
||||
{intl.formatMessage({ id: 'navigation.functions'})}
|
||||
{intl.formatMessage({ id: 'navigation.functions' })}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="screenshots" href="/#screenshots">
|
||||
{intl.formatMessage({ id: 'navigation.screenshots'})}
|
||||
{intl.formatMessage({ id: 'navigation.screenshots' })}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="vad-kostar-det" href="/#vad-kostar-det">
|
||||
{intl.formatMessage({ id: 'navigation.whatdoesitcost'})}
|
||||
{intl.formatMessage({ id: 'navigation.whatdoesitcost' })}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
|
@ -1,8 +1,7 @@
|
|||
import React from 'react'
|
||||
import { formatPrice } from '../utils/intl'
|
||||
import SectionTitle from './SectionTitle'
|
||||
import Icon from './Icon'
|
||||
import DownloadButtons from './DownloadButtons'
|
||||
import Icon from './Icon'
|
||||
import SectionTitle from './SectionTitle'
|
||||
|
||||
const price = 12
|
||||
|
|
@ -1,11 +1,19 @@
|
|||
import classnames from 'classnames'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
interface SectionProps {
|
||||
bg?: string
|
||||
children: ReactNode
|
||||
id?: string
|
||||
padding?: string
|
||||
}
|
||||
|
||||
const Section = ({
|
||||
bg = 'bg-gray-100',
|
||||
padding = 'py-8 md:py-20',
|
||||
children,
|
||||
id,
|
||||
}) => {
|
||||
}: SectionProps) => {
|
||||
return (
|
||||
<section className={classnames('grid grid grid-main', bg, padding)} id={id}>
|
||||
<div className="items-center grid gap-y-5 md:gap-y-20 col-start-3 col-end-4 grid-cols-1 md:grid-cols-2">
|
|
@ -1,4 +1,9 @@
|
|||
const SectionTitle = ({ text, title }) => {
|
||||
interface SectionTitleProps {
|
||||
text?: string
|
||||
title: string
|
||||
}
|
||||
|
||||
const SectionTitle = ({ text, title }: SectionTitleProps) => {
|
||||
return (
|
||||
<div className="mb-16 text-center space-y-5">
|
||||
<h2 className="text-4xl md:text-5xl font-bold leading-tight text-gray-800">
|
|
@ -1,6 +1,7 @@
|
|||
import { TimelineEvent } from '../data/timelineEvents'
|
||||
import Link from './Link'
|
||||
|
||||
const dateFormat = (date) => {
|
||||
const dateFormat = (date: string) => {
|
||||
const parsedDate = new Date(date)
|
||||
|
||||
return Intl
|
||||
|
@ -11,7 +12,11 @@ const dateFormat = (date) => {
|
|||
: `${parsedDate.getFullYear()}-${parsedDate.getMonth()}`
|
||||
}
|
||||
|
||||
const Timeline = ({ events }) => {
|
||||
interface TimelineProps {
|
||||
events: TimelineEvent[]
|
||||
}
|
||||
|
||||
const Timeline = ({ events }: TimelineProps) => {
|
||||
return (
|
||||
<ul className="max-w-2xl border-gray-200 md:mx-auto md:border-l-2 space-y-4 md:space-y-12">
|
||||
{events.map(({ date, media, importantDates, overview }) => (
|
|
@ -1,13 +0,0 @@
|
|||
export const H1 = ({ children }) => {
|
||||
return (
|
||||
<h1 className="mb-5 text-4xl md:text-5xl font-semibold text-gray-800">
|
||||
{children}
|
||||
</h1>
|
||||
)
|
||||
}
|
||||
|
||||
export const H2 = ({ children }) => {
|
||||
return (
|
||||
<h2 className="mb-5 text-5xl font-semibold text-gray-800">{children}</h2>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import { ReactNode } from 'react'
|
||||
|
||||
interface H1Props {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export const H1 = ({ children }: H1Props) => {
|
||||
return (
|
||||
<h1 className="mb-5 text-4xl md:text-5xl font-semibold text-gray-800">
|
||||
{children}
|
||||
</h1>
|
||||
)
|
||||
}
|
||||
|
||||
interface H2Props {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export const H2 = ({ children }: H2Props) => {
|
||||
return (
|
||||
<h2 className="mb-5 text-5xl font-semibold text-gray-800">{children}</h2>
|
||||
)
|
||||
}
|
|
@ -33,5 +33,5 @@ test('handles missing Intl', () => {
|
|||
|
||||
expect(screen.getAllByText(/2021-01/i)[0]).toBeInTheDocument()
|
||||
|
||||
global.intl = intl
|
||||
global.Intl = intl
|
||||
})
|
|
@ -1,15 +0,0 @@
|
|||
const en =
|
||||
{
|
||||
'navigation.home': 'Home',
|
||||
'navigation.functions': 'Functions',
|
||||
'navigation.screenshots': 'Screenshots',
|
||||
'navigation.whatdoesitcost': 'What does it cost?',
|
||||
'general.title': 'Öppna skolplattformen',
|
||||
'general.description': `Whether you have three or seven children - there is a lot to keep in mind. Absence report number 17 in February. What is the name of the substitute
|
||||
this week. The gym clothes. A poorly functioning school platform
|
||||
who eats time and energy? There is no room for that. So we have
|
||||
built a better one. With all the information you need as a parent.
|
||||
Faster and above all - much less hassle.`
|
||||
}
|
||||
|
||||
export default en;
|
|
@ -0,0 +1,15 @@
|
|||
const en = {
|
||||
'navigation.home': 'Home',
|
||||
'navigation.functions': 'Functions',
|
||||
'navigation.screenshots': 'Screenshots',
|
||||
'navigation.whatdoesitcost': 'What does it cost?',
|
||||
'general.title': 'Öppna skolplattformen',
|
||||
'general.description': `Whether you have three or seven children - there is a lot to keep in mind. Absence report number 17 in February. What is the name of the substitute
|
||||
this week. The gym clothes. A poorly functioning school platform
|
||||
who eats time and energy? There is no room for that. So we have
|
||||
built a better one. With all the information you need as a parent.
|
||||
Faster and above all - much less hassle.`,
|
||||
}
|
||||
|
||||
export default en
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
import sv from "./sv";
|
||||
import en from "./en";
|
||||
|
||||
export default { en, sv }
|
|
@ -0,0 +1,5 @@
|
|||
import sv from './sv'
|
||||
import en from './en'
|
||||
|
||||
export default { en, sv }
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
const sv =
|
||||
{
|
||||
'navigation.home': 'Hem',
|
||||
'navigation.functions': 'Funktioner',
|
||||
'navigation.screenshots': 'Screenshots',
|
||||
'navigation.whatdoesitcost': 'Vad kostar det?',
|
||||
'general.title': 'Öppna skolplattformen',
|
||||
'general.description': `Oavsett om du har tre eller sju barn – det är mycket att hålla
|
||||
reda på. Frånvaroanmälan nummer 17 i februari. Vad vikarien heter
|
||||
den här veckan. Gympakläderna. En dåligt fungerande Skolplattform
|
||||
som äter tid och ork? Det finns inte plats för det. Så vi har
|
||||
byggt en bättre. Med all information du behöver som förälder.
|
||||
Snabbare och framförallt – mycket mindre krångel.`
|
||||
}
|
||||
|
||||
export default sv;
|
|
@ -0,0 +1,16 @@
|
|||
const sv = {
|
||||
'navigation.home': 'Hem',
|
||||
'navigation.functions': 'Funktioner',
|
||||
'navigation.screenshots': 'Screenshots',
|
||||
'navigation.whatdoesitcost': 'Vad kostar det?',
|
||||
'general.title': 'Öppna skolplattformen',
|
||||
'general.description': `Oavsett om du har tre eller sju barn – det är mycket att hålla
|
||||
reda på. Frånvaroanmälan nummer 17 i februari. Vad vikarien heter
|
||||
den här veckan. Gympakläderna. En dåligt fungerande Skolplattform
|
||||
som äter tid och ork? Det finns inte plats för det. Så vi har
|
||||
byggt en bättre. Med all information du behöver som förälder.
|
||||
Snabbare och framförallt – mycket mindre krångel.`,
|
||||
}
|
||||
|
||||
export default sv
|
||||
|
|
@ -1,16 +1,30 @@
|
|||
import { ReactNode } from 'react'
|
||||
import Link from '../components/Link'
|
||||
|
||||
const fixEvent = (date) => ({
|
||||
const fixEvent = (date: string) => ({
|
||||
date,
|
||||
description: 'Öppna skolplattformen släpper ny fix',
|
||||
})
|
||||
|
||||
const sabotageEvent = (date) => ({
|
||||
const sabotageEvent = (date: string) => ({
|
||||
date,
|
||||
description: 'Kommun saboterar Öppna Skolplattformen',
|
||||
})
|
||||
|
||||
export const events = [
|
||||
interface Event {
|
||||
date: string
|
||||
description: string
|
||||
link?: string
|
||||
}
|
||||
|
||||
export interface TimelineEvent {
|
||||
overview: ReactNode
|
||||
date: string
|
||||
importantDates?: Event[]
|
||||
media?: Event[]
|
||||
}
|
||||
|
||||
export const events: TimelineEvent[] = [
|
||||
{
|
||||
overview: (
|
||||
<p>
|
|
@ -0,0 +1,17 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
||||
|
||||
declare module '*.png' {
|
||||
const value: any
|
||||
export = value
|
||||
}
|
||||
|
||||
declare module '*.jpg' {
|
||||
const value: any
|
||||
export = value
|
||||
}
|
||||
|
||||
declare module '*.svg' {
|
||||
const value: any
|
||||
export = value
|
||||
}
|
|
@ -1,21 +1,11 @@
|
|||
const withImages = require('next-images')
|
||||
|
||||
// next.config.js
|
||||
module.exports = {
|
||||
...withImages(),
|
||||
i18n: {
|
||||
localeDetection: false,
|
||||
locales: ['sv', 'en'],
|
||||
defaultLocale: 'sv',
|
||||
domains: [
|
||||
{
|
||||
domain: 'skolplattformen.org',
|
||||
defaultLocale: 'sv',
|
||||
},
|
||||
{
|
||||
domain: 'localhost',
|
||||
defaultLocale: 'sv',
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
...withImages(),
|
||||
i18n: {
|
||||
localeDetection: false,
|
||||
locales: ['sv', 'en'],
|
||||
defaultLocale: 'sv',
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/classnames": "^2.2.11",
|
||||
"classnames": "^2.2.6",
|
||||
"next": "^10.0.7",
|
||||
"next-images": "^1.4.0",
|
||||
|
@ -24,12 +25,17 @@
|
|||
"lint": "eslint './**/*.js'",
|
||||
"test": "is-ci test:ci test:watch",
|
||||
"test:ci": "jest",
|
||||
"test:watch": "jest --watch"
|
||||
"test:watch": "jest --watch",
|
||||
"typecheck": "tsc --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.4.0",
|
||||
"@testing-library/jest-dom": "5.11.9",
|
||||
"@testing-library/react": "11.2.5",
|
||||
"@types/gtag.js": "^0.0.4",
|
||||
"@types/jest": "^26.0.22",
|
||||
"@types/react": "^17.0.3",
|
||||
"@types/react-scroll": "^1.8.2",
|
||||
"autoprefixer": "^10.2.4",
|
||||
"babel-eslint": "10.1.0",
|
||||
"eslint": "7.20.0",
|
||||
|
@ -42,6 +48,7 @@
|
|||
"jest-watch-typeahead": "0.6.1",
|
||||
"postcss": "^8.2.6",
|
||||
"prettier": "2.2.1",
|
||||
"tailwindcss": "^2.0.3"
|
||||
"tailwindcss": "^2.0.3",
|
||||
"typescript": "^4.2.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import 'swiper/swiper-bundle.min.css'
|
||||
import '../styles/global.css'
|
||||
import { IntlProvider } from "react-intl"
|
||||
|
||||
import Layout from '../components/Layout'
|
||||
import Footer from '../components/Footer'
|
||||
import Header from '../components/Header'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
import { IntlProvider } from 'react-intl'
|
||||
import { pageview } from '../components/gtag'
|
||||
import messages from '../content/locale/'
|
||||
|
||||
|
@ -14,7 +14,7 @@ export default function App({ Component, pageProps }) {
|
|||
const router = useRouter()
|
||||
const { locale, defaultLocale } = router
|
||||
|
||||
const currentMessages = messages[locale];
|
||||
const currentMessages = messages[locale]
|
||||
|
||||
// Google analytics
|
||||
useEffect(() => {
|
||||
|
@ -28,11 +28,11 @@ export default function App({ Component, pageProps }) {
|
|||
}, [router.events])
|
||||
|
||||
return (
|
||||
<IntlProvider
|
||||
locale={locale}
|
||||
defaultLocale={defaultLocale}
|
||||
messages={currentMessages}
|
||||
>
|
||||
<IntlProvider
|
||||
locale={locale}
|
||||
defaultLocale={defaultLocale}
|
||||
messages={currentMessages}
|
||||
>
|
||||
<Layout pageTitle="Skolplattformen">
|
||||
<Header />
|
||||
<Component {...pageProps} />
|
|
@ -1,11 +1,6 @@
|
|||
import Document, { Html, Head, Main, NextScript } from 'next/document'
|
||||
|
||||
class MyDocument extends Document {
|
||||
static async getInitialProps(ctx) {
|
||||
const initialProps = await Document.getInitialProps(ctx)
|
||||
return { ...initialProps }
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html lang="sv-SE">
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
export const formatNumber = (input, options = {}) =>
|
||||
new Intl.NumberFormat('sv-SE', options).format(input)
|
||||
|
||||
export const formatPrice = (input) =>
|
||||
formatNumber(input, {
|
||||
currency: 'SEK',
|
||||
minimumFractionDigits: 0,
|
||||
style: 'currency',
|
||||
})
|
|
@ -0,0 +1,11 @@
|
|||
export const formatNumber = (
|
||||
input: number,
|
||||
options: Intl.NumberFormatOptions = {}
|
||||
) => new Intl.NumberFormat('sv-SE', options).format(input)
|
||||
|
||||
export const formatPrice = (input: number) =>
|
||||
formatNumber(input, {
|
||||
currency: 'SEK',
|
||||
minimumFractionDigits: 0,
|
||||
style: 'currency',
|
||||
})
|
|
@ -4,9 +4,16 @@ import { render as rtlRender } from '@testing-library/react'
|
|||
import { IntlProvider } from 'react-intl'
|
||||
import messages from '../content/locale'
|
||||
|
||||
function render(ui, { locale = 'sv', ...renderOptions } = {}) {
|
||||
function render(
|
||||
ui: React.ReactElement,
|
||||
{ locale = 'sv', ...renderOptions } = {}
|
||||
) {
|
||||
function Wrapper({ children }) {
|
||||
return <IntlProvider locale={locale} messages={messages[locale]}>{children}</IntlProvider>
|
||||
return (
|
||||
<IntlProvider locale={locale} messages={messages[locale]}>
|
||||
{children}
|
||||
</IntlProvider>
|
||||
)
|
||||
}
|
||||
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions })
|
||||
}
|
|
@ -741,6 +741,11 @@
|
|||
dependencies:
|
||||
"@babel/types" "^7.3.0"
|
||||
|
||||
"@types/classnames@^2.2.11":
|
||||
version "2.2.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.11.tgz#2521cc86f69d15c5b90664e4829d84566052c1cf"
|
||||
integrity sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw==
|
||||
|
||||
"@types/graceful-fs@^4.1.2":
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15"
|
||||
|
@ -748,6 +753,11 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/gtag.js@^0.0.4":
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/gtag.js/-/gtag.js-0.0.4.tgz#81427d703b5a889b4fd1c37eaae6f2a6db228e5a"
|
||||
integrity sha512-rn4yaEfryXklAtsiai4xHNUgLjGD8AEPQBNSCTa5QrvccFYYBWJQRGwaHiv2U/wdIzcjOuyyBC23YgdQfq5u/Q==
|
||||
|
||||
"@types/hoist-non-react-statics@^3.3.1":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
||||
|
@ -783,6 +793,14 @@
|
|||
jest-diff "^26.0.0"
|
||||
pretty-format "^26.0.0"
|
||||
|
||||
"@types/jest@^26.0.22":
|
||||
version "26.0.22"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.22.tgz#8308a1debdf1b807aa47be2838acdcd91e88fbe6"
|
||||
integrity sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw==
|
||||
dependencies:
|
||||
jest-diff "^26.0.0"
|
||||
pretty-format "^26.0.0"
|
||||
|
||||
"@types/json-schema@^7.0.6":
|
||||
version "7.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
|
||||
|
@ -808,7 +826,14 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
|
||||
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
|
||||
|
||||
"@types/react@*":
|
||||
"@types/react-scroll@^1.8.2":
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-scroll/-/react-scroll-1.8.2.tgz#44bbbadabb9014517eb865d6fa47937535a2234a"
|
||||
integrity sha512-oavV6BZLfaIghX4JSmrm6mJkeVayQlmsFx1Rz8ffGjMngHAI/juZkRZM/zV/H5D0pGqjzACvBmKYUU4YBecwLg==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*", "@types/react@^17.0.3":
|
||||
version "17.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.3.tgz#ba6e215368501ac3826951eef2904574c262cc79"
|
||||
integrity sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==
|
||||
|
@ -6144,6 +6169,11 @@ typedarray-to-buffer@^3.1.5:
|
|||
dependencies:
|
||||
is-typedarray "^1.0.0"
|
||||
|
||||
typescript@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
|
||||
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
|
||||
|
||||
union-value@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
|
||||
|
|
Loading…
Reference in New Issue