|
| 1 | +import React, { useEffect, useState } from 'react' |
| 2 | + |
| 3 | +import { |
| 4 | + ArticleGuide, |
| 5 | + useProductGuidesContext, |
| 6 | +} from 'components/context/ProductGuidesContext' |
| 7 | +import { useTranslation } from 'components/hooks/useTranslation' |
| 8 | +import { ArticleCard } from './ArticleCard' |
| 9 | +import { DropdownMenu } from '@primer/components' |
| 10 | +import { ItemInput } from '@primer/components/lib/ActionList/List' |
| 11 | + |
| 12 | +const PAGE_SIZE = 9 |
| 13 | +export const ArticleCards = () => { |
| 14 | + const { t } = useTranslation('product_guides') |
| 15 | + const guideTypes: Record<string, string> = t('guide_types') |
| 16 | + const { allTopics, includeGuides } = useProductGuidesContext() |
| 17 | + const [numVisible, setNumVisible] = useState(PAGE_SIZE) |
| 18 | + const [typeFilter, setTypeFilter] = useState<ItemInput | undefined>() |
| 19 | + const [topicFilter, setTopicFilter] = useState<ItemInput | undefined>() |
| 20 | + const [filteredResults, setFilteredResults] = useState<Array<ArticleGuide>>([]) |
| 21 | + |
| 22 | + useEffect(() => { |
| 23 | + setNumVisible(PAGE_SIZE) |
| 24 | + setFilteredResults( |
| 25 | + (includeGuides || []).filter((card) => { |
| 26 | + const matchesType = card.type === typeFilter?.key |
| 27 | + const matchesTopic = card.topics.some((key) => key === topicFilter?.key) |
| 28 | + return (typeFilter?.key ? matchesType : true) && (topicFilter?.key ? matchesTopic : true) |
| 29 | + }) |
| 30 | + ) |
| 31 | + }, [typeFilter, topicFilter]) |
| 32 | + |
| 33 | + const isUserFiltering = typeFilter !== undefined || topicFilter !== undefined |
| 34 | + |
| 35 | + const guides = isUserFiltering ? filteredResults : includeGuides || [] |
| 36 | + |
| 37 | + const types = Object.entries(guideTypes).map(([key, val]) => { |
| 38 | + return ( |
| 39 | + {text: val, key: key} |
| 40 | + ) |
| 41 | + }) as ItemInput[] |
| 42 | + |
| 43 | + types.unshift({text: t('filters.all'), key: undefined}) |
| 44 | + |
| 45 | + const topics = allTopics?.map((topic) => { |
| 46 | + return ( |
| 47 | + {text: topic, key: topic} |
| 48 | + ) |
| 49 | + }) as ItemInput[] |
| 50 | + |
| 51 | + topics.unshift({text: t('filters.all'), key: undefined}) |
| 52 | + |
| 53 | + return ( |
| 54 | + <div> |
| 55 | + <label htmlFor="guide-filter-form">{t('filter_instructions')}</label> |
| 56 | + <form name="guide-filter-form" className="mt-2 mb-5 d-flex d-flex"> |
| 57 | + <div data-testid="card-filter-types"> |
| 58 | + <label htmlFor="type" className="text-uppercase f6 color-fg-muted d-block"> |
| 59 | + {t('filters.type')} |
| 60 | + </label> |
| 61 | + <DropdownMenu |
| 62 | + aria-label="guide types" |
| 63 | + data-testid="types-dropdown" |
| 64 | + placeholder={t('filters.all')} |
| 65 | + items={types} |
| 66 | + selectedItem={typeFilter} |
| 67 | + onChange={setTypeFilter} /> |
| 68 | + </div> |
| 69 | + |
| 70 | + <div data-testid="card-filter-topics" className="mx-4"> |
| 71 | + <label htmlFor="topic" className="text-uppercase f6 color-fg-muted d-block"> |
| 72 | + {t('filters.topic')} |
| 73 | + </label> |
| 74 | + <DropdownMenu |
| 75 | + aria-label="guide topics" |
| 76 | + data-testid="topics-dropdown" |
| 77 | + placeholder={t('filters.all')} |
| 78 | + items={topics} |
| 79 | + selectedItem={topicFilter} |
| 80 | + onChange={setTopicFilter} /> |
| 81 | + </div> |
| 82 | + </form> |
| 83 | + |
| 84 | + <div role="status" className="color-fg-muted"> |
| 85 | + {guides.length === 0 |
| 86 | + ? t('guides_found.none') |
| 87 | + : guides.length === 1 |
| 88 | + ? t('guides_found.one') |
| 89 | + : t('guides_found.multiple').replace('{n}', guides.length)} |
| 90 | + </div> |
| 91 | + |
| 92 | + <div className="d-flex flex-wrap mr-0 mr-md-n6 mr-lg-n8"> |
| 93 | + {guides.slice(0, numVisible).map((card) => { |
| 94 | + return <ArticleCard key={card.href} card={card} typeLabel={guideTypes[card.type]} /> |
| 95 | + })} |
| 96 | + </div> |
| 97 | + |
| 98 | + {guides.length > numVisible && ( |
| 99 | + <button |
| 100 | + className="col-12 mt-5 text-center text-bold color-fg-accent btn-link" |
| 101 | + onClick={() => setNumVisible(numVisible + PAGE_SIZE)} |
| 102 | + > |
| 103 | + {t('load_more')} |
| 104 | + </button> |
| 105 | + )} |
| 106 | + </div> |
| 107 | + ) |
| 108 | +} |
0 commit comments