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

feat(web): add info card #16931

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2991578
feat: init
thorkellmani Nov 18, 2024
bffca01
feat: stfuf
thorkellmani Nov 18, 2024
c31e2e4
fix:error message
thorkellmani Nov 19, 2024
c691abc
fix: merge
thorkellmani Nov 27, 2024
35efc7d
fix: responseive
thorkellmani Nov 27, 2024
94a8575
fix: switch in inline
thorkellmani Nov 27, 2024
ea88272
fix: use wrapper
thorkellmani Nov 27, 2024
55ae166
fix: add layout switch
thorkellmani Nov 28, 2024
f2bba67
fix: disable tags
thorkellmani Nov 28, 2024
ca42520
Merge branch 'main' into feat/add-info-card
thorkellmani Dec 3, 2024
be27cbe
fix: make whole card clickable
thorkellmani Dec 3, 2024
c28ada2
chore: reduce columns
thorkellmani Dec 3, 2024
0f766cb
feat: updated status logic
thorkellmani Dec 4, 2024
b9bdb0e
feat: add time values
thorkellmani Dec 4, 2024
1507cab
feat: add time part
thorkellmani Dec 4, 2024
51e8a4a
Merge branch 'main' into feat/add-info-card
thorkellmani Dec 4, 2024
b3ce418
Merge branch 'main' into feat/add-info-card
thorkellmani Dec 4, 2024
ec8e7b8
chore: change props name
thorkellmani Dec 4, 2024
1eb0cb0
Merge remote-tracking branch 'refs/remotes/origin/feat/add-info-card'…
thorkellmani Dec 4, 2024
c11145c
chore: remove conidtional
thorkellmani Dec 4, 2024
859964a
chore: remove imports
thorkellmani Dec 4, 2024
9ec4527
fix: wrong message id
thorkellmani Dec 4, 2024
3301830
fix: add /en locale
thorkellmani Dec 11, 2024
50c504d
Merge branch 'main' into feat/updated-stats-logic
thorkellmani Dec 11, 2024
f80f403
Merge branch 'main' into feat/add-info-card
thorkellmani Dec 11, 2024
dfb70bd
Merge branch 'main' into feat/updated-stats-logic
thorkellmani Dec 12, 2024
e907c68
fix: merge
thorkellmani Dec 12, 2024
b8fd9c9
fix: undefined if empty array
thorkellmani Dec 12, 2024
569651a
Merge branch 'main' into feat/add-info-card
thorkellmani Dec 12, 2024
e2080c6
Merge branch 'main' into feat/updated-stats-logic
thorkellmani Dec 12, 2024
e2a0b6a
feat: multiple updates
thorkellmani Dec 12, 2024
d156f3c
chore: simplify card
thorkellmani Dec 12, 2024
5fd3723
chore: fix color
thorkellmani Dec 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ codemagic.yaml
/apps/services/regulations-admin-backend/ @island-is/hugsmidjan
/apps/services/user-profile/ @island-is/hugsmidjan @island-is/juni @island-is/aranja
/apps/web/components/Grant/ @island-is/hugsmidjan
/apps/web/components/PlazaCard/ @island-is/hugsmidjan
/apps/web/components/InfoCard/ @island-is/hugsmidjan
/apps/web/screens/Grants/ @island-is/hugsmidjan
/apps/web/screens/Regulations/ @island-is/hugsmidjan
/apps/web/components/Regulations/ @island-is/hugsmidjan
Expand Down
217 changes: 217 additions & 0 deletions apps/web/components/InfoCard/DetailedInfoCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
import * as React from 'react'
import { useEffect, useState } from 'react'
import { useWindowSize } from 'react-use'

import {
Box,
FocusableBox,
GridColumn,
GridContainer,
GridRow,
Icon,
IconMapIcon,
Inline,
LinkV2,
Stack,
Tag,
Text,
} from '@island.is/island-ui/core'
import { ActionCardProps } from '@island.is/island-ui/core/types'
import { theme } from '@island.is/island-ui/theme'
import { isDefined } from '@island.is/shared/utils'

import { BaseProps } from './InfoCard'
import * as styles from './InfoCard.css'

const eyebrowColor = 'blueberry600'

export type DetailedProps = BaseProps & {
logo?: string
logoAlt?: string
subEyebrow?: string
//max 5 lines
detailLines?: Array<{
icon: IconMapIcon
text: string
}>
tags?: Array<ActionCardProps['tag']>
}

export const DetailedInfoCard = ({
title,
description,
size = 'medium',
eyebrow,
subEyebrow,
detailLines,
tags,
logo,
logoAlt,
link,
}: DetailedProps) => {
const [isTablet, setIsTablet] = useState(false)
const { width } = useWindowSize()

useEffect(() => {
if (width < theme.breakpoints.lg) {
return setIsTablet(true)
}
setIsTablet(false)
}, [width])
Comment on lines +55 to +60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect use of return in useEffect

The useEffect hook should not return the result of setIsTablet(true) as it is not a cleanup function. Returning a value from useEffect other than a cleanup function can lead to unexpected behavior.

Apply this diff to fix the issue:

 useEffect(() => {
   if (width < theme.breakpoints.lg) {
-    return setIsTablet(true)
+    setIsTablet(true)
   } else {
     setIsTablet(false)
   }
-  }, [width])
+}, [width])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
if (width < theme.breakpoints.lg) {
return setIsTablet(true)
}
setIsTablet(false)
}, [width])
useEffect(() => {
if (width < theme.breakpoints.lg) {
setIsTablet(true)
} else {
setIsTablet(false)
}
}, [width])


const renderLogo = () => {
if (!logo) {
return null
}

return (
<Box style={{ flex: '0 0 40px' }}>
<img height={40} src={logo} alt={logoAlt} />
</Box>
)
}

const renderDetails = () => {
if (!detailLines?.length) {
return null
}

return (
<Box marginTop={2}>
<Stack space={1}>
{detailLines?.slice(0, 5).map((d, index) => (
<Box
key={index}
display="flex"
flexDirection={'row'}
alignItems="center"
>
<Icon
icon={d.icon}
size="small"
type="outline"
color="blue400"
useStroke
/>
<Box marginLeft={2}>
<Text variant="medium">{d.text}</Text>
</Box>
</Box>
))}
</Stack>
</Box>
)
}

const renderTags = () => {
if (!tags?.length) {
return null
}

return (
<Inline space={1}>
{tags
.map((tag) => {
if (!tag) {
return null
}
return (
<Tag disabled variant={tag.variant}>
{tag.label}
</Tag>
)
})
.filter(isDefined)}
</Inline>
Comment on lines +114 to +125
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add a unique key prop to elements in list rendering

When rendering a list of items in React using map, each element should have a unique key prop. In the tags.map() function, the Tag components are missing a key prop, which can lead to rendering issues and warnings in the console.

Apply this diff to fix the issue:

 {tags
-  .map((tag) => {
+  .map((tag, index) => {
     if (!tag) {
       return null
     }
     return (
-      <Tag disabled variant={tag.variant}>
+      <Tag key={tag.label} disabled variant={tag.variant}>
         {tag.label}
       </Tag>
     )
   })
   .filter(isDefined)}

Ensure that tag.label is unique. If it is not guaranteed to be unique, consider using an index or another unique identifier.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.map((tag) => {
if (!tag) {
return null
}
return (
<Tag disabled variant={tag.variant}>
{tag.label}
</Tag>
)
})
.filter(isDefined)}
</Inline>
.map((tag, index) => {
if (!tag) {
return null
}
return (
<Tag key={tag.label} disabled variant={tag.variant}>
{tag.label}
</Tag>
)
})
.filter(isDefined)}
</Inline>
🧰 Tools
🪛 Biome (1.9.4)

[error] 119-119: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

)
}

const renderHeader = () => {
return (
<Box
display="flex"
flexDirection="row"
justifyContent="spaceBetween"
marginBottom={3}
>
{subEyebrow ? (
<Box>
<Text fontWeight="semiBold" variant="eyebrow" color={eyebrowColor}>
{eyebrow}
</Text>
<Text fontWeight="light" variant="eyebrow" color={eyebrowColor}>
{subEyebrow}
</Text>
</Box>
) : (
<Text variant="eyebrow" color={eyebrowColor}>
{eyebrow}
</Text>
)}
{renderLogo()}
</Box>
)
}

const renderContent = () => {
if (size === 'large' && !isTablet) {
return (
<GridContainer>
<GridRow direction="row">
<GridColumn span="8/12">
<Text variant="h3" color="blue400">
{title}
</Text>
{description && (
<Box flexGrow={1} marginTop={1}>
<Text>{description}</Text>
</Box>
)}
</GridColumn>
<GridColumn span="4/12">{renderDetails()}</GridColumn>
</GridRow>
</GridContainer>
)
}
return (
<>
<Text variant="h3" color="blue400">
{title}
</Text>
{description && (
<Box marginTop={1}>
<Text>{description}</Text>
</Box>
)}
{renderDetails()}
</>
)
}

return (
<FocusableBox
className={
size === 'large'
? styles.infoCardWide
: size === 'small'
? styles.infoCardSmall
: styles.infoCard
}
component={LinkV2}
href={link.href}
background="white"
borderColor="white"
borderWidth="standard"
width="full"
borderRadius="standard"
>
<Box width="full" paddingX={4} paddingY={3}>
{renderHeader()}
{renderContent()}
<Box marginTop={3} display="flex" justifyContent="spaceBetween">
{renderTags()}
</Box>
</Box>
</FocusableBox>
)
}
17 changes: 17 additions & 0 deletions apps/web/components/InfoCard/InfoCard.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { style } from '@vanilla-extract/css'

export const infoCardSmall = style({
maxWidth: 310,
})

export const infoCard = style({
maxWidth: 477,
})

export const infoCardWide = style({
maxWidth: 978,
})

export const wideTitleBox = style({
flexGrow: 2,
})
31 changes: 31 additions & 0 deletions apps/web/components/InfoCard/InfoCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as React from 'react'

import { DetailedInfoCard, DetailedProps } from './DetailedInfoCard'
import { SimpleInfoCard } from './SimpleInfoCard'

export interface BaseProps {
title: string
description: string
eyebrow: string
size: 'large' | 'medium' | 'small'
link: {
label: string
href: string
}
}

export type InfoCardProps =
| (BaseProps & {
variant?: 'simple'
})
| (DetailedProps & {
variant: 'detailed'
})

export const InfoCard = (props: InfoCardProps) => {
if (props.variant === 'detailed') {
return <DetailedInfoCard {...props} />
} else {
return <SimpleInfoCard {...props} />
}
}
49 changes: 49 additions & 0 deletions apps/web/components/InfoCard/InfoCardWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useEffect, useState } from 'react'
import { useWindowSize } from 'react-use'

import { Inline, Stack } from '@island.is/island-ui/core'
import { theme } from '@island.is/island-ui/theme'

import { InfoCard, InfoCardProps } from './InfoCard'

export type InfoCardItemProps = Omit<InfoCardProps, 'size' | 'variant'>

interface Props {
cards: Array<InfoCardItemProps>
variant?: 'detailed' | 'simple'
columns?: 1 | 2 | 3
}

export const InfoCardWrapper = ({ cards, variant, columns }: Props) => {
const [isMobile, setIsMobile] = useState(false)
const { width } = useWindowSize()

useEffect(() => {
if (width < theme.breakpoints.md) {
return setIsMobile(true)
}
setIsMobile(false)
}, [width])

if (columns === 1 || isMobile) {
return (
<Stack space={3}>
{cards.map((c) => (
<InfoCard variant={variant} size={'large'} {...c} />
))}
Comment on lines +31 to +33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add missing key prop to mapped InfoCard components

React requires a unique key prop when rendering elements in an array to optimize rendering and maintain component state correctly.

-          <InfoCard variant={variant} size={'large'} {...c} />
+          <InfoCard key={c.id ?? c.title} variant={variant} size={'large'} {...c} />

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Biome (1.9.4)

[error] 32-32: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

</Stack>
)
}

return (
<Inline space={3}>
{cards.map((c) => (
<InfoCard
variant={variant}
size={columns === 3 ? 'small' : 'medium'}
{...c}
/>
))}
Comment on lines +40 to +46
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add missing key prop to mapped InfoCard components

Similar to the previous issue, add a unique key prop here as well.

         <InfoCard
+          key={c.id ?? c.title}
           variant={variant}
           size={columns === 3 ? 'small' : 'medium'}
           {...c}
         />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{cards.map((c) => (
<InfoCard
variant={variant}
size={columns === 3 ? 'small' : 'medium'}
{...c}
/>
))}
{cards.map((c) => (
<InfoCard
key={c.id ?? c.title}
variant={variant}
size={columns === 3 ? 'small' : 'medium'}
{...c}
/>
))}
🧰 Tools
🪛 Biome (1.9.4)

[error] 41-45: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

</Inline>
)
}
Loading
Loading