This repository has been archived by the owner on Nov 14, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add filter with tests * Fix filter and dynamic import the filter * Add functionality for favorites * Update tests * Remove commented code
- Loading branch information
1 parent
99390b6
commit 8a3debc
Showing
14 changed files
with
5,314 additions
and
1,823 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,21 @@ | ||
{ | ||
"extends": ["eslint:recommended", "next/core-web-vitals", "prettier"], | ||
"rules": {} | ||
"plugins": [ | ||
"@typescript-eslint" | ||
], | ||
"parser": "@typescript-eslint/parser", | ||
"env": { | ||
"jest": true | ||
}, | ||
"extends": [ | ||
"eslint:recommended", | ||
"plugin:@typescript-eslint/recommended", | ||
"next/core-web-vitals", | ||
"prettier" | ||
], | ||
"rules": { | ||
"no-unused-vars": "off", | ||
"@typescript-eslint/no-unused-vars": [ | ||
"error" | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
'use client' | ||
|
||
import { Session } from '@/app/program/program' | ||
import { filter, useFilter } from '@/components/filter/filter' | ||
import { SimpleTalk } from '@/app/program/SimpleTalk' | ||
import { ProgramFilter } from '@/app/program/ProgramFilter' | ||
import { useMemo } from 'react' | ||
import styles from '@/app/program/page.module.css' | ||
|
||
interface FilteredSessionProps { | ||
sessions: Session[] | ||
} | ||
|
||
const getStatCount = (format: Session['format']) => (session: Session) => session.format === format | ||
|
||
export default function FilteredSessions({ sessions }: FilteredSessionProps) { | ||
const { filterState, updateFilter } = useFilter() | ||
const filteredSessions = filter(sessions, filterState) | ||
|
||
const stats = useMemo(() => { | ||
return { | ||
all: filteredSessions.length, | ||
presentation: filteredSessions.filter(getStatCount('presentation')).length, | ||
lightningTalks: filteredSessions.filter(getStatCount('lightning-talk')).length, | ||
favorites: filteredSessions.filter( | ||
(session) => filterState.favorites?.includes(session.sessionId), | ||
).length, | ||
} | ||
}, [filterState.favorites, filteredSessions]) | ||
|
||
const toggleFavorite = (session: Session, isFavorite: boolean) => { | ||
if (isFavorite) { | ||
return updateFilter({ | ||
favorites: filterState.favorites?.filter((favorite) => favorite !== session.sessionId), | ||
}) | ||
} | ||
return updateFilter({ favorites: [...(filterState.favorites ?? []), session.sessionId] }) | ||
} | ||
|
||
return ( | ||
<div> | ||
<ProgramFilter filter={filterState} onFilterChange={updateFilter} statistics={stats} /> | ||
{filteredSessions | ||
.filter( | ||
(session) => | ||
!filterState.format || | ||
session.format === filterState.format || | ||
filterState.favorites?.includes(session.sessionId), | ||
) | ||
.map((session) => { | ||
const isFavorite = filterState.favorites?.includes(session.sessionId) ?? false | ||
return ( | ||
<SimpleTalk key={session.sessionId} session={session}> | ||
<button | ||
aria-label={!isFavorite ? 'Add to favorites' : 'Remove from favorites'} | ||
className={styles.favorite} | ||
onClick={() => toggleFavorite(session, isFavorite)} | ||
> | ||
{isFavorite ? '💔' : '❤️'} | ||
</button> | ||
</SimpleTalk> | ||
) | ||
})} | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
.filter_container { | ||
container-type: inline-size; | ||
} | ||
|
||
.filter { | ||
background-color: var(--dark-blue-background); | ||
padding: 1rem; | ||
display: grid; | ||
gap: 1rem; | ||
grid-template-areas: | ||
"day" | ||
"language" | ||
"format" | ||
"clear-filter"; | ||
} | ||
|
||
.filter p { | ||
font-weight: bold; | ||
color: var(--big-text-color); | ||
} | ||
|
||
.day { | ||
grid-area: day; | ||
} | ||
|
||
.language { | ||
grid-area: language; | ||
} | ||
|
||
.format { | ||
grid-area: format; | ||
} | ||
|
||
.clear_filter { | ||
grid-area: clear-filter; | ||
} | ||
|
||
.button { | ||
font-family: inherit; | ||
padding: 0.5rem 2rem; | ||
cursor: pointer; | ||
border: none; | ||
margin: 0.2rem; | ||
font-size: 1.2em; | ||
font-weight: 700; | ||
} | ||
|
||
.button[data-active=true] { | ||
background-color: white; | ||
} | ||
|
||
@container (min-width: 500px) { | ||
.filter { | ||
grid-template-areas: | ||
"day language" | ||
"format format" | ||
"clear-filter clear-filter"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import { Filter } from '@/components/filter/filter' | ||
import styles from './ProgramFilter.module.css' | ||
|
||
// weekdays | ||
const WEDNESDAY = 3 | ||
const THURSDAY = 4 | ||
|
||
interface Props { | ||
filter: Filter | ||
onFilterChange: (filter: Filter) => void | ||
statistics: {all: number, presentation: number, lightningTalks: number, favorites: number} | ||
} | ||
|
||
export const ProgramFilter = ({ onFilterChange, filter, statistics = { presentation: 0, favorites: 0, lightningTalks: 0, all: 0} }: Props) => { | ||
const clearFilter = () => | ||
onFilterChange({ | ||
format: undefined, | ||
language: undefined, | ||
weekday: undefined, | ||
}) | ||
|
||
return ( | ||
<section className={styles.filter_container}> | ||
<div className={styles.filter}> | ||
<div className={styles.day}> | ||
<p>Day</p> | ||
<button | ||
disabled | ||
className={`${styles.button} is-dark-blue button`} | ||
data-active={filter.weekday === undefined} | ||
onClick={() => onFilterChange({ weekday: undefined })} | ||
> | ||
Both | ||
</button> | ||
<button | ||
disabled | ||
className={`${styles.button} is-dark-blue button`} | ||
data-active={filter.weekday === WEDNESDAY} | ||
onClick={() => onFilterChange({ weekday: WEDNESDAY })} | ||
> | ||
Wednesday | ||
</button> | ||
<button | ||
disabled | ||
className={`${styles.button} is-dark-blue button`} | ||
data-active={filter.weekday === THURSDAY} | ||
onClick={() => onFilterChange({ weekday: THURSDAY })} | ||
> | ||
Thursday | ||
</button> | ||
</div> | ||
<div className={styles.language}> | ||
<p>Language</p> | ||
<button | ||
className={`${styles.button} is-dark-blue button`} | ||
data-active={filter.language === undefined} | ||
onClick={() => onFilterChange({ language: undefined })} | ||
> | ||
Both | ||
</button> | ||
<button | ||
className={`${styles.button} is-dark-blue button`} | ||
data-active={filter.language === 'no'} | ||
onClick={() => onFilterChange({ language: 'no' })} | ||
> | ||
Norwegian | ||
</button> | ||
<button | ||
className={`${styles.button} is-dark-blue button`} | ||
data-active={filter.language === 'en'} | ||
onClick={() => onFilterChange({ language: 'en' })} | ||
> | ||
English | ||
</button> | ||
</div> | ||
<div className={styles.format}> | ||
<p>Format</p> | ||
<button | ||
className={`${styles.button} is-dark-blue button`} | ||
data-active={filter.format === undefined} | ||
onClick={() => onFilterChange({ format: undefined })} | ||
> | ||
All ({statistics.all}) | ||
</button> | ||
<button | ||
className={`${styles.button} is-dark-blue button`} | ||
data-active={filter.format === 'presentation'} | ||
onClick={() => onFilterChange({ format: 'presentation' })} | ||
> | ||
Presentation ({statistics.presentation}) | ||
</button> | ||
<button | ||
className={`${styles.button} is-dark-blue button`} | ||
data-active={filter.format === 'lightning-talk'} | ||
onClick={() => onFilterChange({ format: 'lightning-talk' })} | ||
> | ||
Lightning talks ({statistics.lightningTalks}) | ||
</button> | ||
<button | ||
className={`${styles.button} is-dark-blue button`} | ||
data-active={filter.format === 'favorites'} | ||
onClick={() => | ||
onFilterChange({ | ||
format: 'favorites', | ||
}) | ||
} | ||
> | ||
My favorites ({statistics.favorites}) | ||
</button> | ||
</div> | ||
<div className={styles.clear_filter}> | ||
<button className={`${styles.button} is-dark-blue button`} onClick={clearFilter}> | ||
Clear filters | ||
</button> | ||
</div> | ||
</div> | ||
</section> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,18 @@ | ||
import { Session, SessionFormat } from './program' | ||
import styles from './page.module.css' | ||
import { SessionGroup } from './SessionGroup' | ||
import { fetchProgram } from '@/app/program/fetchProgram' | ||
import dynamic from "next/dynamic"; | ||
|
||
const FilteredSessions = dynamic(() => import('./FilteredSessions'), { ssr: false }) | ||
|
||
export default async function ProgramPage() { | ||
const data = await fetchProgram() | ||
|
||
const sessionsGroupedByType = data.sessions.reduce((groups, session) => { | ||
groups[session.format] = groups[session.format] ?? [] | ||
groups[session.format].push(session) | ||
return groups | ||
}, {} as Record<SessionFormat, Session[]>) | ||
const sessions = data.sessions.filter(session => session.format !== "workshop") | ||
|
||
return ( | ||
<article> | ||
<h1 className={styles.program_title}>JavaZone Program 2023</h1> | ||
<SessionGroup group={'presentation'} sessions={sessionsGroupedByType['presentation']} /> | ||
<SessionGroup group={'lightning-talk'} sessions={sessionsGroupedByType['lightning-talk']} /> | ||
<FilteredSessions sessions={sessions} /> | ||
</article> | ||
) | ||
} |
Oops, something went wrong.
8a3debc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
2023-javazone-no – ./
2023.javazone.no
2023-javazone-no-javabin.vercel.app
2023-javazone-no-git-main-javabin.vercel.app
2023-javazone-no.vercel.app