diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f4bc0d23934e..fab199409190 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -132,7 +132,6 @@ 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/screens/Grants/ @island-is/hugsmidjan /apps/web/screens/Regulations/ @island-is/hugsmidjan /apps/web/components/Regulations/ @island-is/hugsmidjan diff --git a/apps/web/components/PlazaCard/PlazaCard.css.ts b/apps/web/components/PlazaCard/PlazaCard.css.ts deleted file mode 100644 index bb5b75f34928..000000000000 --- a/apps/web/components/PlazaCard/PlazaCard.css.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { style } from '@vanilla-extract/css' - -export const container = style({ - maxWidth: '476px', -}) diff --git a/apps/web/components/real.ts b/apps/web/components/real.ts index e03cf0f7843f..bcf7523b139d 100644 --- a/apps/web/components/real.ts +++ b/apps/web/components/real.ts @@ -12,7 +12,6 @@ */ export * from './Card/Card' -export * from './PlazaCard/PlazaCard' export * from './Header/Header' export * from './SearchInput/SearchInput' export * from './LanguageToggler/LanguageToggler' diff --git a/apps/web/screens/Grants/Grant/GrantSidebar.tsx b/apps/web/screens/Grants/Grant/GrantSidebar.tsx index c2f9fe31939c..5a1941ee5b2a 100644 --- a/apps/web/screens/Grants/Grant/GrantSidebar.tsx +++ b/apps/web/screens/Grants/Grant/GrantSidebar.tsx @@ -9,7 +9,6 @@ import { Stack, Text, } from '@island.is/island-ui/core' -import { useLocale } from '@island.is/localization' import { Locale } from '@island.is/shared/types' import { isDefined } from '@island.is/shared/utils' import { InstitutionPanel } from '@island.is/web/components' diff --git a/apps/web/screens/Grants/SearchResults/SearchResults.tsx b/apps/web/screens/Grants/SearchResults/SearchResults.tsx index 1dbb89da54d9..4902b1229a2b 100644 --- a/apps/web/screens/Grants/SearchResults/SearchResults.tsx +++ b/apps/web/screens/Grants/SearchResults/SearchResults.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useState } from 'react' +import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react' import { useIntl } from 'react-intl' import { useWindowSize } from 'react-use' import debounce from 'lodash/debounce' @@ -16,6 +16,7 @@ import { BreadCrumbItem, Breadcrumbs, FilterInput, + Pagination, Text, } from '@island.is/island-ui/core' import { theme } from '@island.is/island-ui/theme' @@ -55,9 +56,11 @@ export interface SearchState { organization?: Array } +const PAGE_SIZE = 8 + const GrantsSearchResultsPage: CustomScreen = ({ locale, - initialGrants, + initialGrantList, tags, }) => { const { formatMessage } = useIntl() @@ -67,7 +70,12 @@ const GrantsSearchResultsPage: CustomScreen = ({ const parentUrl = linkResolver('styrkjatorg', [], locale).href const currentUrl = linkResolver('styrkjatorgsearch', [], locale).href - const [grants, setGrants] = useState>(initialGrants ?? []) + const [grants, setGrants] = useState>( + initialGrantList?.items ?? [], + ) + const [totalHits, setTotalHits] = useState( + initialGrantList?.total ?? 0, + ) const [searchState, setSearchState] = useState() const [initialRender, setInitialRender] = useState(true) @@ -89,7 +97,7 @@ const GrantsSearchResultsPage: CustomScreen = ({ const organizations = searchParams.getAll('organization') setSearchState({ - page: page ? Number.parseInt(page) : undefined, + page: page ? Number.parseInt(page) : 1, query: searchParams.get('query') ?? undefined, status: statuses.length ? statuses : undefined, category: categories.length ? categories : undefined, @@ -98,6 +106,13 @@ const GrantsSearchResultsPage: CustomScreen = ({ }) }, []) + const totalPages = useMemo(() => { + if (!totalHits) { + return + } + return totalHits > PAGE_SIZE ? Math.ceil(totalHits / PAGE_SIZE) : 1 + }, [totalHits]) + const updateUrl = useCallback(() => { if (!searchState) { return @@ -134,7 +149,7 @@ const GrantsSearchResultsPage: CustomScreen = ({ organizations: searchState?.organization, page: searchState?.page, search: searchState?.query, - size: 8, + size: PAGE_SIZE, statuses: searchState?.status, types: searchState?.type, }, @@ -143,6 +158,7 @@ const GrantsSearchResultsPage: CustomScreen = ({ .then((res) => { if (res.data) { setGrants(res.data.getGrants.items) + setTotalHits(res.data.getGrants.total) } else if (res.error) { setGrants([]) console.error('Error fetching grants', res.error) @@ -199,7 +215,7 @@ const GrantsSearchResultsPage: CustomScreen = ({ const onResetFilter = () => { setSearchState({ - page: undefined, + page: 1, query: undefined, status: undefined, category: undefined, @@ -210,18 +226,18 @@ const GrantsSearchResultsPage: CustomScreen = ({ } const hitsMessage = useMemo(() => { - if (!grants) { + if (!totalHits) { return } - if (grants.length === 1) { + if (totalHits === 1) { return formatMessage(m.search.resultFound, { - arg: {grants.length}, + arg: {totalHits}, }) } return formatMessage(m.search.resultsFound, { - arg: {grants.length}, + arg: {totalHits}, }) - }, [formatMessage, grants]) + }, [formatMessage, totalHits]) return ( = ({ locale={locale} /> + {totalPages && totalPages > 1 ? ( + + ( + { + updateSearchStateValue('page', page) + }} + > + {children} + + )} + /> + + ) : undefined} )} {isMobile && ( @@ -335,19 +373,19 @@ const GrantsSearchResultsPage: CustomScreen = ({ interface GrantsHomeProps { locale: Locale - initialGrants?: Array + initialGrantList?: GrantList tags?: Array } const GrantsSearchResults: CustomScreen = ({ - initialGrants, + initialGrantList, tags, customPageData, locale, }) => { return ( { }, }), ]) + return { - initialGrants: getGrants.items, + initialGrantList: getGrants, tags: getGenericTagsInTagGroups ?? undefined, locale: locale as Locale, themeConfig: { diff --git a/apps/web/screens/Grants/SearchResults/SearchResultsContent.tsx b/apps/web/screens/Grants/SearchResults/SearchResultsContent.tsx index d57cece2196d..b95daf770f97 100644 --- a/apps/web/screens/Grants/SearchResults/SearchResultsContent.tsx +++ b/apps/web/screens/Grants/SearchResults/SearchResultsContent.tsx @@ -1,13 +1,12 @@ +import { useState } from 'react' +import { useIntl } from 'react-intl' import { useWindowSize } from 'react-use' import format from 'date-fns/format' -import { useRouter } from 'next/router' -import { Box, Inline, Text } from '@island.is/island-ui/core' +import { Box, Button, InfoCardGrid, Text } from '@island.is/island-ui/core' import { theme } from '@island.is/island-ui/theme' -import { useLocale } from '@island.is/localization' import { Locale } from '@island.is/shared/types' import { isDefined } from '@island.is/shared/utils' -import { PlazaCard } from '@island.is/web/components' import { Grant } from '@island.is/web/graphql/schema' import { useLinkResolver } from '@island.is/web/hooks' @@ -21,93 +20,111 @@ interface Props { } export const SearchResultsContent = ({ grants, subheader, locale }: Props) => { - const { formatMessage } = useLocale() - const router = useRouter() + const { formatMessage } = useIntl() const { linkResolver } = useLinkResolver() const { width } = useWindowSize() const isMobile = width <= theme.breakpoints.md const isTablet = width <= theme.breakpoints.lg && width > theme.breakpoints.md + const [isGridLayout, setIsGridLayout] = useState(true) + return ( <> {!isMobile && ( - + {subheader} + )} {grants?.length ? ( - - {grants?.map((grant) => { - if (!grant) { - return null - } + { + if (!grant || !grant.applicationId) { + return null + } + + const status = parseStatus(grant, formatMessage, locale) - const status = parseStatus(grant, formatMessage, locale) - return ( - - {grant.applicationId && ( - { - router.push( - linkResolver( - 'styrkjatorggrant', - [grant?.applicationId ?? ''], - locale, - ).href, - ) - }, - icon: 'arrowForward', - }} - detailLines={[ - grant.dateFrom && grant.dateTo - ? { - icon: 'calendar' as const, - text: `${format( - new Date(grant.dateFrom), - 'dd.MM.', - )} - ${format( - new Date(grant.dateTo), - 'dd.MM.yyyy', - )}`, - } - : null, - status.deadlineStatus - ? { - icon: 'time' as const, - //todo: fix when the text is ready - text: status.deadlineStatus, - } - : undefined, - grant.categoryTags - ? { - icon: 'informationCircle' as const, - text: grant.categoryTags - .map((ct) => ct.title) - .filter(isDefined) - .join(', '), - } - : undefined, - ].filter(isDefined)} - /> - )} - - ) - })} - + return { + id: grant.id, + eyebrow: grant.fund?.title ?? grant.name ?? '', + subEyebrow: grant.fund?.parentOrganization?.title, + title: grant.name ?? '', + description: grant.description ?? '', + logo: + grant.fund?.featuredImage?.url ?? + grant.fund?.parentOrganization?.logo?.url ?? + '', + logoAlt: + grant.fund?.featuredImage?.title ?? + grant.fund?.parentOrganization?.logo?.title ?? + '', + tags: status.applicationStatus + ? [ + generateStatusTag( + status.applicationStatus, + formatMessage, + ), + ].filter(isDefined) + : undefined, + link: { + label: formatMessage(m.general.seeMore), + href: linkResolver( + 'styrkjatorggrant', + [grant?.applicationId ?? ''], + locale, + ).href, + }, + detailLines: [ + grant.dateFrom && grant.dateTo + ? { + icon: 'calendar' as const, + text: `${format( + new Date(grant.dateFrom), + 'dd.MM.yyyy', + )} - ${format(new Date(grant.dateTo), 'dd.MM.yyyy')}`, + } + : null, + { + icon: 'time' as const, + text: status.deadlineStatus, + }, + grant.categoryTags + ? { + icon: 'informationCircle' as const, + text: grant.categoryTags + .map((ct) => ct.title) + .filter(isDefined) + .join(', '), + } + : undefined, + ].filter(isDefined), + } + }) + .filter(isDefined) ?? [] + } + /> ) : undefined} {!grants?.length && ( - format(date, stringFormat, { - locale: locale === 'is' ? localeIS : localeEn, - }) +): string | undefined => { + try { + return format(date, stringFormat, { + locale: locale === 'is' ? localeIS : localeEn, + }) + } catch (e) { + console.warn('Error formatting date') + return + } +} export const containsTimePart = (date: string) => date.includes('T') @@ -33,15 +38,18 @@ export const parseStatus = ( ): Status => { switch (grant.status) { case GrantStatus.Closed: { + const date = grant.dateTo + ? formatDate(new Date(grant.dateTo), locale) + : undefined return { applicationStatus: 'closed', - deadlineStatus: grant.dateTo + deadlineStatus: date ? formatMessage( - containsTimePart(grant.dateTo) + containsTimePart(date) ? m.search.applicationWasOpenToAndWith : m.search.applicationWasOpenTo, { - arg: formatDate(new Date(grant.dateTo), locale), + arg: date, }, ) : formatMessage(m.search.applicationClosed), @@ -49,22 +57,28 @@ export const parseStatus = ( } } case GrantStatus.ClosedOpeningSoon: { + const date = grant.dateFrom + ? formatDate(new Date(grant.dateFrom), locale) + : undefined return { applicationStatus: 'closed', - deadlineStatus: grant.dateFrom + deadlineStatus: date ? formatMessage(m.search.applicationOpensAt, { - arg: formatDate(new Date(grant.dateFrom), locale), + arg: date, }) : formatMessage(m.search.applicationClosed), note: grant.statusText ?? undefined, } } case GrantStatus.ClosedOpeningSoonWithEstimation: { + const date = grant.dateFrom + ? formatDate(new Date(grant.dateFrom), locale, 'MMMM yyyy') + : undefined return { applicationStatus: 'closed', - deadlineStatus: grant.dateFrom + deadlineStatus: date ? formatMessage(m.search.applicationEstimatedOpensAt, { - arg: formatDate(new Date(grant.dateFrom), locale, 'MMMM yyyy'), + arg: date, }) : formatMessage(m.search.applicationClosed), note: grant.statusText ?? undefined, @@ -85,15 +99,18 @@ export const parseStatus = ( } } case GrantStatus.Open: { + const date = grant.dateTo + ? formatDate(new Date(grant.dateTo), locale, 'dd.MMMM.') + : undefined return { applicationStatus: 'open', - deadlineStatus: grant.dateTo + deadlineStatus: date ? formatMessage( - containsTimePart(grant.dateTo) + containsTimePart(date) ? m.search.applicationOpensToWithDay : m.search.applicationOpensTo, { - arg: formatDate(new Date(grant.dateTo), locale, 'dd.MMMM.'), + arg: date, }, ) : formatMessage(m.search.applicationOpen), diff --git a/apps/web/screens/queries/Grants.ts b/apps/web/screens/queries/Grants.ts index 7518d29e81d1..c62a073a363b 100644 --- a/apps/web/screens/queries/Grants.ts +++ b/apps/web/screens/queries/Grants.ts @@ -5,6 +5,7 @@ import { nestedFields, slices } from './fragments' export const GET_GRANTS_QUERY = gql` query GetGrants($input: GetGrantsInput!) { getGrants(input: $input) { + total items { id name diff --git a/libs/cms/src/lib/cms.elasticsearch.service.ts b/libs/cms/src/lib/cms.elasticsearch.service.ts index 2d1945a91db4..181c25af2111 100644 --- a/libs/cms/src/lib/cms.elasticsearch.service.ts +++ b/libs/cms/src/lib/cms.elasticsearch.service.ts @@ -728,7 +728,6 @@ export class CmsElasticsearchService { size, from: (page - 1) * size, }) - return { total: grantListResponse.body.hits.total.value, items: grantListResponse.body.hits.hits.map((response) => diff --git a/libs/cms/src/lib/models/grant.model.ts b/libs/cms/src/lib/models/grant.model.ts index ba4238814907..c119d9387395 100644 --- a/libs/cms/src/lib/models/grant.model.ts +++ b/libs/cms/src/lib/models/grant.model.ts @@ -162,7 +162,6 @@ export const mapGrant = ({ fields, sys }: IGrant): Grant => { files: (fields.grantFiles ?? []).map((file) => mapAsset(file)) ?? [], supportLinks: (fields.grantSupportLinks ?? []).map((link) => mapLink(link)) ?? [], - categoryTags: fields.grantCategoryTags ? fields.grantCategoryTags.map((tag) => mapGenericTag(tag)) : undefined, diff --git a/libs/cms/src/lib/search/importers/grants.service.ts b/libs/cms/src/lib/search/importers/grants.service.ts index 3f2336eb2e1e..ead7003d046b 100644 --- a/libs/cms/src/lib/search/importers/grants.service.ts +++ b/libs/cms/src/lib/search/importers/grants.service.ts @@ -17,13 +17,11 @@ import { isDefined } from '@island.is/shared/utils' @Injectable() export class GrantsSyncService implements CmsSyncProvider { processSyncData(entries: processSyncDataInput) { - // only process grants that we consider not to be empty and dont have circular structures + // only process grants that we consider not to be empty return entries.filter( // eslint-disable-next-line @typescript-eslint/no-explicit-any (entry: Entry): entry is IGrant => - entry.sys.contentType.sys.id === 'grant' && - entry.fields.grantName && - !isCircular(entry), + entry.sys.contentType.sys.id === 'grant' && entry.fields.grantName, ) } diff --git a/libs/island-ui/core/src/index.ts b/libs/island-ui/core/src/index.ts index dfce8e49b301..9136b255cb47 100644 --- a/libs/island-ui/core/src/index.ts +++ b/libs/island-ui/core/src/index.ts @@ -60,6 +60,7 @@ export * from './lib/VisuallyHidden/VisuallyHidden' export * from './lib/Drawer/Drawer' export * from './lib/ProblemTemplate/ProblemTemplate' export * from './lib/ProblemTemplate/ProblemTemplate.css' +export * from './lib/InfoCardGrid' // Cards export * from './lib/LinkCard/LinkCard' diff --git a/apps/web/components/PlazaCard/PlazaCard.tsx b/libs/island-ui/core/src/lib/InfoCardGrid/DetailedInfoCard.tsx similarity index 52% rename from apps/web/components/PlazaCard/PlazaCard.tsx rename to libs/island-ui/core/src/lib/InfoCardGrid/DetailedInfoCard.tsx index f18c1e2da060..899f15be1450 100644 --- a/apps/web/components/PlazaCard/PlazaCard.tsx +++ b/libs/island-ui/core/src/lib/InfoCardGrid/DetailedInfoCard.tsx @@ -1,46 +1,45 @@ -import * as React from 'react' - import { Box, - Button, + GridColumn, + GridContainer, + GridRow, Icon, IconMapIcon, + Inline, Stack, Tag, Text, -} from '@island.is/island-ui/core' -import { ActionCardProps } from '@island.is/island-ui/core/types' +} from '../..' +import { isDefined } from '@island.is/shared/utils' -import * as styles from './PlazaCard.css' +import { BaseProps } from './InfoCard' +import { ActionCardProps } from '../ActionCard/types' const eyebrowColor = 'blueberry600' -interface Props { - title: string - text: string +export type DetailedProps = BaseProps & { logo?: string logoAlt?: string - eyebrow?: string subEyebrow?: string + //max 5 lines detailLines?: Array<{ icon: IconMapIcon text: string }> - tag?: ActionCardProps['tag'] - cta?: ActionCardProps['cta'] + tags?: Array } -export const PlazaCard = ({ +export const DetailedInfoCard = ({ title, - text, + description, + size = 'medium', eyebrow, subEyebrow, detailLines, - tag, + tags, logo, logoAlt, - cta, -}: Props) => { +}: DetailedProps) => { const renderLogo = () => { if (!logo) { return null @@ -53,37 +52,6 @@ export const PlazaCard = ({ ) } - const renderEyebrow = () => { - if (!eyebrow) { - return null - } - - return ( - - {subEyebrow ? ( - - - {eyebrow} - - - {subEyebrow} - - - ) : ( - - {eyebrow} - - )} - {renderLogo()} - - ) - } - const renderDetails = () => { if (!detailLines?.length) { return null @@ -92,7 +60,7 @@ export const PlazaCard = ({ return ( - {detailLines?.map((d, index) => ( + {detailLines?.slice(0, 5).map((d, index) => ( - + {d.text} @@ -116,56 +84,98 @@ export const PlazaCard = ({ ) } - const renderTag = () => { - if (!tag) { + const renderTags = () => { + if (!tags?.length) { return null } - return {tag.label} + return ( + + {tags + .map((tag, index) => { + if (!tag) { + return null + } + return ( + + {tag.label} + + ) + }) + .filter(isDefined)} + + ) } - const renderCta = () => { - if (!cta) { - return null - } - + const renderHeader = () => { return ( - + {subEyebrow ? ( + + + {eyebrow} + + + {subEyebrow} + + + ) : ( + + {eyebrow} + + )} + {renderLogo()} + ) } - return ( - - - {renderEyebrow()} + const renderContent = () => { + if (size === 'large') { + return ( + + + + + {title} + + {description && ( + + {description} + + )} + + {renderDetails()} + + + ) + } + return ( + <> {title} - {text && ( + {description && ( - {text} + {description} )} {renderDetails()} - - {renderTag()} - {renderCta()} - + + ) + } + + return ( + <> + {renderHeader()} + {renderContent()} + + {renderTags()} - + ) } diff --git a/libs/island-ui/core/src/lib/InfoCardGrid/InfoCard.css.ts b/libs/island-ui/core/src/lib/InfoCardGrid/InfoCard.css.ts new file mode 100644 index 000000000000..4c27d84947de --- /dev/null +++ b/libs/island-ui/core/src/lib/InfoCardGrid/InfoCard.css.ts @@ -0,0 +1,20 @@ +import { style } from '@vanilla-extract/css' + +export const infoCardSmall = style({ + maxWidth: 310, + minHeight: 450, +}) + +export const infoCard = style({ + maxWidth: 477, + minHeight: 432, +}) + +export const infoCardWide = style({ + maxWidth: 978, + minHeight: 318, +}) + +export const wideTitleBox = style({ + flexGrow: 2, +}) diff --git a/libs/island-ui/core/src/lib/InfoCardGrid/InfoCard.tsx b/libs/island-ui/core/src/lib/InfoCardGrid/InfoCard.tsx new file mode 100644 index 000000000000..339d50453d1a --- /dev/null +++ b/libs/island-ui/core/src/lib/InfoCardGrid/InfoCard.tsx @@ -0,0 +1,72 @@ +import { useEffect, useState } from 'react' +import { useWindowSize } from 'react-use' + +import { theme } from '@island.is/island-ui/theme' + +import { DetailedInfoCard, DetailedProps } from './DetailedInfoCard' +import { SimpleInfoCard } from './SimpleInfoCard' +import * as styles from './InfoCard.css' +import { Box, FocusableBox, LinkV2 } from '../..' + +export interface BaseProps { + id: string + 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 = ({ size, ...restOfProps }: InfoCardProps) => { + const [isMobile, setIsMobile] = useState(false) + const { width } = useWindowSize() + + useEffect(() => { + if (width < theme.breakpoints.md) { + return setIsMobile(true) + } + setIsMobile(false) + }, [width]) + + const cardSize = isMobile ? 'small' : size + + return ( + + + {restOfProps.variant === 'detailed' ? ( + + ) : ( + + )} + + + ) +} diff --git a/libs/island-ui/core/src/lib/InfoCardGrid/InfoCardGrid.tsx b/libs/island-ui/core/src/lib/InfoCardGrid/InfoCardGrid.tsx new file mode 100644 index 000000000000..73b454a9cabb --- /dev/null +++ b/libs/island-ui/core/src/lib/InfoCardGrid/InfoCardGrid.tsx @@ -0,0 +1,50 @@ +import { useEffect, useState } from 'react' +import { useWindowSize } from 'react-use' + +import { Inline, Stack } from '../..' +import { theme } from '@island.is/island-ui/theme' + +import { InfoCard, InfoCardProps } from './InfoCard' + +export type InfoCardItemProps = Omit + +interface Props { + cards: Array + variant?: 'detailed' | 'simple' + columns?: 1 | 2 | 3 +} + +export const InfoCardGrid = ({ 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 ( + + {cards.map((c) => ( + + ))} + + ) + } + + return ( + + {cards.map((c, index) => ( + + ))} + + ) +} diff --git a/libs/island-ui/core/src/lib/InfoCardGrid/SimpleInfoCard.tsx b/libs/island-ui/core/src/lib/InfoCardGrid/SimpleInfoCard.tsx new file mode 100644 index 000000000000..34b633891737 --- /dev/null +++ b/libs/island-ui/core/src/lib/InfoCardGrid/SimpleInfoCard.tsx @@ -0,0 +1,71 @@ +import { Box, GridColumn, GridContainer, GridRow, Text } from '../..' + +import { BaseProps } from './InfoCard' + +const eyebrowColor = 'blueberry600' + +export const SimpleInfoCard = ({ + title, + description, + size = 'medium', + eyebrow, +}: BaseProps) => { + const renderHeader = () => { + if (!eyebrow) { + return + } + return ( + + + {eyebrow} + + + ) + } + + const renderContent = () => { + if (size === 'large') { + return ( + + + + + {title} + + {description && ( + + {description} + + )} + + + + ) + } + return ( + <> + + {title} + + {description && ( + + {description} + + )} + + ) + } + + return ( + <> + {renderHeader()} + {renderContent()} + + ) +} diff --git a/libs/island-ui/core/src/lib/InfoCardGrid/index.ts b/libs/island-ui/core/src/lib/InfoCardGrid/index.ts new file mode 100644 index 000000000000..29cb05d74923 --- /dev/null +++ b/libs/island-ui/core/src/lib/InfoCardGrid/index.ts @@ -0,0 +1 @@ +export { InfoCardGrid } from './InfoCardGrid'