diff --git a/components/actions/ActionHighlightCard.js b/components/actions/ActionHighlightCard.tsx similarity index 89% rename from components/actions/ActionHighlightCard.js rename to components/actions/ActionHighlightCard.tsx index 998df39e..3ea7d71b 100644 --- a/components/actions/ActionHighlightCard.js +++ b/components/actions/ActionHighlightCard.tsx @@ -1,5 +1,4 @@ import React, { useContext } from 'react'; -import PropTypes from 'prop-types'; import { Card, CardBody, CardTitle, Badge } from 'reactstrap'; import styled from 'styled-components'; import { transparentize } from 'polished'; @@ -7,6 +6,7 @@ import PlanContext from 'context/plan'; import { useTheme } from 'common/theme'; import EmbedContext from 'context/embed'; import { cleanActionStatus } from 'common/preprocess'; +import { ActionHighlightListAction } from './ActionHighlightsList'; import { getStatusColorForAction } from 'common/ActionStatusSummary'; import { ActionLink } from 'common/links'; import Icon from 'components/common/Icon'; @@ -61,14 +61,14 @@ const StyledCardTitle = styled(CardTitle)` margin-bottom: 0; `; -const ImgArea = styled.div` +const ImgArea = styled.div<{ bgcolor?: string }>` min-height: 9rem; position: relative; background-color: ${(props) => props.bgcolor || props.theme.themeColors.light}; `; -const ImgBg = styled.div` +const ImgBg = styled.div<{ background: string }>` height: 9rem; background-image: url(${(props) => props.background}); background-position: center; @@ -104,7 +104,15 @@ const ActionNumber = styled.div` } `; -function ActionHighlightCard(props) { +type ActionHighlightCardProps = { + action: ActionHighlightListAction; + imageUrl?: string; + hideIdentifier?: boolean; +}; + +// TODO: FIX typechecking + +function ActionHighlightCard(props: ActionHighlightCardProps) { const { action, imageUrl, hideIdentifier } = props; const plan = useContext(PlanContext); const embed = useContext(EmbedContext); @@ -163,23 +171,4 @@ function ActionHighlightCard(props) { ); } -ActionHighlightCard.defaultProps = { - hideIdentifier: false, - imageUrl: undefined, -}; - -ActionHighlightCard.propTypes = { - action: PropTypes.shape({ - identifier: PropTypes.string, - name: PropTypes.string, - status: PropTypes.shape({ - name: PropTypes.string, - identifier: PropTypes.string, - }), - completion: PropTypes.number, - }).isRequired, - imageUrl: PropTypes.string, - hideIdentifier: PropTypes.bool, -}; - export default React.memo(ActionHighlightCard); diff --git a/components/actions/ActionHighlightsList.js b/components/actions/ActionHighlightsList.tsx similarity index 69% rename from components/actions/ActionHighlightsList.js rename to components/actions/ActionHighlightsList.tsx index 7e3ea7c7..0b3a691a 100644 --- a/components/actions/ActionHighlightsList.js +++ b/components/actions/ActionHighlightsList.tsx @@ -1,12 +1,13 @@ /* eslint-disable max-classes-per-file */ import React, { useContext } from 'react'; -import PropTypes from 'prop-types'; import { gql } from '@apollo/client'; import { Query } from '@apollo/client/react/components'; import styled from 'styled-components'; import { Row, Col } from 'reactstrap'; -import LazyLoad from 'react-lazyload'; - +import { + ActionHightlightListQuery, + PlanContextFragment, +} from 'common/__generated__/graphql'; import EmbedContext from 'context/embed'; import Button from 'components/common/Button'; import { getActionTermContext, useTranslation } from 'common/i18n'; @@ -93,28 +94,21 @@ const StyledCardContainer = styled(ReactStrapCol)` .card { height: 100%; } - - .lazyload-wrapper { - width: 100%; - } `; -const ConditionalLazyLoad = (props) => { - if (props.embed?.active) { - return <>{props.children}; - } - return {props.children}; -}; +export type ActionHighlightListAction = NonNullable< + ActionHightlightListQuery['planActions'] +>; -const CardContainer = (props) => { - return ( - - {props.children} - - ); +type ActionCardListProps = { + t: (arg0: string, arg1: Record) => string; + actions: ActionHighlightListAction; + plan: PlanContextFragment; + displayHeader?: boolean; }; -function ActionCardList({ t, actions, plan, displayHeader }) { +function ActionCardList(props: ActionCardListProps) { + const { t, actions, plan, displayHeader } = props; // Components which use the EmbedContext support embedding const embed = useContext(EmbedContext); @@ -122,11 +116,13 @@ function ActionCardList({ t, actions, plan, displayHeader }) { {displayHeader && ( -

{t('recently-updated-actions', getActionTermContext(plan))}

+

+ {t('recently-updated-actions', getActionTermContext(plan) || {})} +

)} - {actions.map((item) => ( - ( + - + ))} {!embed.active && ( @@ -155,26 +151,22 @@ function ActionCardList({ t, actions, plan, displayHeader }) { ); } -ActionCardList.propTypes = { - t: PropTypes.func.isRequired, - actions: PropTypes.arrayOf(PropTypes.shape({})).isRequired, - displayHeader: PropTypes.bool, - plan: PropTypes.shape({ - identifier: PropTypes.string, - }).isRequired, +type ActionHighlightsListProps = { + plan: PlanContextFragment; }; -function ActionHighlightsList(props) { - const { plan, count, displayHeader } = props; +function ActionHighlightsList(props: ActionHighlightsListProps) { + const { plan } = props; const { t } = useTranslation(); const queryParams = { plan: plan.identifier, - first: count ?? 6, + first: 6, orderBy: '-updatedAt', }; + // TODO: Convert to useQuery return ( {({ data, loading, error }) => { @@ -183,25 +175,10 @@ function ActionHighlightsList(props) { return (

{t('error-loading-actions', getActionTermContext(plan))}

); - return ( - - ); + return ; }}
); } -ActionHighlightsList.propTypes = { - count: PropTypes.number, - displayHeader: PropTypes.bool, - plan: PropTypes.shape({ - identifier: PropTypes.string, - }).isRequired, -}; - export default ActionHighlightsList; diff --git a/components/contentblocks/IndicatorHighlightsBlock.tsx b/components/contentblocks/IndicatorHighlightsBlock.tsx index 840f01cf..4b6a3628 100644 --- a/components/contentblocks/IndicatorHighlightsBlock.tsx +++ b/components/contentblocks/IndicatorHighlightsBlock.tsx @@ -23,7 +23,7 @@ const IndicatorHighlightsBlock = ({ id = '' }: CommonContentBlockProps) => { return ( - + ); diff --git a/components/indicators/IndicatorHighlightCard.js b/components/indicators/IndicatorHighlightCard.tsx similarity index 86% rename from components/indicators/IndicatorHighlightCard.js rename to components/indicators/IndicatorHighlightCard.tsx index 489a633a..76e179c1 100644 --- a/components/indicators/IndicatorHighlightCard.js +++ b/components/indicators/IndicatorHighlightCard.tsx @@ -1,9 +1,8 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { Card, CardImgOverlay, CardBody, CardTitle } from 'reactstrap'; import styled from 'styled-components'; -import { getActionTermContext, withTranslation } from '../../common/i18n'; -import { IndicatorLink } from '../../common/links'; +import { getActionTermContext, withTranslation } from 'common/i18n'; +import { IndicatorLink } from 'common/links'; import { usePlan } from 'context/plan'; import { readableColor } from 'polished'; @@ -96,8 +95,16 @@ function beautifyValue(x) { return displayNumber.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' '); } -function IndicatorHighlightCard(props) { - const { t, level, objectid, name, value, unit } = props; +type IndicatorHighlightCardProps = { + level: string | null | undefined; + objectid: string; + name: string; + value?: number; + unit?: string; +}; + +function IndicatorHighlightCard(props: IndicatorHighlightCardProps) { + const { t, level, objectid, name, value = null, unit = '' } = props; const plan = usePlan(); // FIXME: It sucks that we only use the context for the translation key 'action' @@ -108,7 +115,6 @@ function IndicatorHighlightCard(props) { return ( - {/* TODO: animate transition */} @@ -132,17 +138,4 @@ function IndicatorHighlightCard(props) { ); } -IndicatorHighlightCard.defaultProps = { - value: null, - unit: '-', -}; - -IndicatorHighlightCard.propTypes = { - level: PropTypes.string.isRequired, - objectid: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - value: PropTypes.number, - unit: PropTypes.string, -}; - export default withTranslation('common')(IndicatorHighlightCard); diff --git a/components/indicators/IndicatorHighlightsList.js b/components/indicators/IndicatorHighlightsList.js deleted file mode 100644 index 6df6b11a..00000000 --- a/components/indicators/IndicatorHighlightsList.js +++ /dev/null @@ -1,129 +0,0 @@ -/* eslint-disable max-classes-per-file */ -import React from 'react'; -import PropTypes from 'prop-types'; -import { gql, useQuery } from '@apollo/client'; -import { Row, Col } from 'reactstrap'; -import styled from 'styled-components'; -import LazyLoad from 'react-lazyload'; -import { withTranslation } from '../../common/i18n'; -import ContentLoader from '../common/ContentLoader'; -import { IndicatorListLink } from '../../common/links'; - -import IndicatorHighlightCard from './IndicatorHighlightCard'; -import Icon from '../common/Icon'; -import Button from '../common/Button'; - -export const GET_INDICATOR_HIGHLIGHTS = gql` - query IndicatorHightlightList($plan: ID!, $first: Int!, $orderBy: String!) { - planIndicators( - plan: $plan - first: $first - orderBy: $orderBy - hasData: true - hasGoals: true - ) { - id - identifier - name - unit { - id - name - shortName - } - latestValue { - id - value - } - updatedAt - level(plan: $plan) - } - } -`; - -const ListHeader = styled(Col)` - h2 { - font-size: ${(props) => props.theme.fontSizeXl}; - margin-bottom: ${(props) => props.theme.spaces.s400}; - } -`; - -const CardContainer = styled(Col)` - margin-bottom: ${(props) => props.theme.spaces.s150}; - - .lazyload-wrapper { - width: 100%; - } - .card { - height: 100%; - } -`; - -function IndicatorCardList(props) { - const { t, indicators } = props; - - return ( - - -

{t('recently-updated-indicators')}

-
- {indicators.map((item) => ( - - - - - - ))} - - - - - -
- ); -} - -IndicatorCardList.propTypes = { - t: PropTypes.func.isRequired, - indicators: PropTypes.arrayOf(PropTypes.shape({})).isRequired, -}; - -function IndicatorHighlightsList(props) { - const { t, plan } = props; - const queryParams = { - plan: plan.identifier, - first: 6, - orderBy: '-updatedAt', - }; - - const { loading, error, data } = useQuery(GET_INDICATOR_HIGHLIGHTS, { - variables: queryParams, - }); - - if (loading) return ; - if (error) return

{t('error-loading-indicators')}

; - return ; -} - -IndicatorHighlightsList.propTypes = { - t: PropTypes.func.isRequired, - plan: PropTypes.shape({ - identifier: PropTypes.string, - }).isRequired, -}; - -export default withTranslation('common')(IndicatorHighlightsList); diff --git a/components/indicators/IndicatorHighlightsList.tsx b/components/indicators/IndicatorHighlightsList.tsx new file mode 100644 index 00000000..ffebc383 --- /dev/null +++ b/components/indicators/IndicatorHighlightsList.tsx @@ -0,0 +1,130 @@ +/* eslint-disable max-classes-per-file */ +import React from 'react'; +import { gql, useQuery } from '@apollo/client'; +import { Row, Col } from 'reactstrap'; +import styled from 'styled-components'; +import { withTranslation } from 'common/i18n'; +import ContentLoader from 'components/common/ContentLoader'; +import { IndicatorListLink } from 'common/links'; +import type { IndicatorHightlightListQuery } from 'common/__generated__/graphql'; +import IndicatorHighlightCard from './IndicatorHighlightCard'; +import Icon from 'components/common/Icon'; +import Button from 'components/common/Button'; + +export const GET_INDICATOR_HIGHLIGHTS = gql` + query IndicatorHightlightList($plan: ID!, $first: Int!, $orderBy: String!) { + planIndicators( + plan: $plan + first: $first + orderBy: $orderBy + hasData: true + hasGoals: true + ) { + id + identifier + name + unit { + id + name + shortName + } + latestValue { + id + value + } + updatedAt + level(plan: $plan) + } + } +`; + +const ListHeader = styled(Col)` + h2 { + font-size: ${(props) => props.theme.fontSizeXl}; + margin-bottom: ${(props) => props.theme.spaces.s400}; + } +`; + +const CardContainer = styled(Col)` + margin-bottom: ${(props) => props.theme.spaces.s150}; + + .card { + height: 100%; + } +`; + +export type IndicatorHighlightListIndicator = NonNullable< + IndicatorHightlightListQuery['planIndicators'] +>; + +type IndicatorCardListProps = { + t: (arg0: string) => string; + indicators: IndicatorHighlightListIndicator | null | undefined; +}; + +function IndicatorCardList(props: IndicatorCardListProps) { + const { t, indicators } = props; + + return ( + + +

{t('recently-updated-indicators')}

+
+ {indicators?.map( + (item) => + item && ( + + + + ) + )} + + + + + +
+ ); +} + +type IndicatorHighlightsListProps = { + t: (arg0: string) => string; + planIdentifier: string; +}; + +function IndicatorHighlightsList(props: IndicatorHighlightsListProps) { + const { t, planIdentifier } = props; + const queryParams = { + plan: planIdentifier, + first: 6, + orderBy: '-updatedAt', + }; + + const { loading, error, data } = useQuery( + GET_INDICATOR_HIGHLIGHTS, + { + variables: queryParams, + } + ); + + if (loading) return ; + if (error) return

{t('error-loading-indicators')}

; + return ; +} + +export default withTranslation('common')(IndicatorHighlightsList); diff --git a/package.json b/package.json index d6c68166..cdfff15c 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,6 @@ "react-hotkeys-hook": "^3.4.4", "react-inlinesvg": "^3.0.0", "react-intersection-observer": "^9.1.0", - "react-lazyload": "^3.2.0", "react-map-gl": "^7.0.19", "react-medium-image-zoom": "^5.1.1", "react-piwik": "^1.12.0", diff --git a/yarn.lock b/yarn.lock index e835a6a8..b4e7a52c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17176,7 +17176,6 @@ __metadata: react-hotkeys-hook: ^3.4.4 react-inlinesvg: ^3.0.0 react-intersection-observer: ^9.1.0 - react-lazyload: ^3.2.0 react-map-gl: ^7.0.19 react-medium-image-zoom: ^5.1.1 react-piwik: ^1.12.0 @@ -21352,16 +21351,6 @@ __metadata: languageName: node linkType: hard -"react-lazyload@npm:^3.2.0": - version: 3.2.0 - resolution: "react-lazyload@npm:3.2.0" - peerDependencies: - react: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - react-dom: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 871a88c698a80c53ead85c293b493ac67c94e5980590094128c371e3f1e3d5bc17841ead1e7830771c87ece7fe677a6c9c323f3e6ce392a5f6f0328a36c38fe7 - languageName: node - linkType: hard - "react-lifecycles-compat@npm:^3.0.2, react-lifecycles-compat@npm:^3.0.4": version: 3.0.4 resolution: "react-lifecycles-compat@npm:3.0.4"