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

Support templateable category pages #167

Merged
merged 8 commits into from
Sep 28, 2023
462 changes: 341 additions & 121 deletions common/__generated__/graphql.ts

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions common/__generated__/possible_types.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions components/actions/ActionContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import PopoverTip from 'components/common/PopoverTip';
import IndicatorCausalVisualisation from 'components/indicators/IndicatorCausalVisualisation';
import AttributesBlock from 'components/common/AttributesBlock';
import CategoryTags from './CategoryTags';
import ActionContactFormBlock from 'components/contentblocks/ActionContactFormBlock';
import ExpandableFeedbackFormBlock from 'components/contentblocks/ExpandableFeedbackFormBlock';
import ActionPhase from './ActionPhase';
import ActionStatus from './ActionStatus';
import ActionImpact from './ActionImpact';
Expand Down Expand Up @@ -491,7 +491,7 @@ function ActionContentBlock(props: ActionContentBlockProps) {
/>
);
case 'ActionContactFormBlock': {
return <ActionContactFormBlock {...block} action={action} />;
return <ExpandableFeedbackFormBlock {...block} action={action} />;
}
case 'ActionContentCategoryTypeBlock': {
const categories = action.categories.filter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,11 @@ const SegmentLabel = styled.span`
}
`;

function CategoryMetaBar(props) {
const { category } = props;
interface Props {
category: string;
}

function CategoryMetaBar({ category }: Props) {
const plan = useContext(PlanContext);
const { t } = useTranslation(['actions']);
const theme = useTheme();
Expand Down Expand Up @@ -150,9 +153,4 @@ function CategoryMetaBar(props) {
);
}

// TODO: prop types and defaults
CategoryMetaBar.propTypes = {};

CategoryMetaBar.defaultProps = {};

export default CategoryMetaBar;
134 changes: 134 additions & 0 deletions components/categories/CategoryPageContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import React from 'react';
import { Container, Row } from 'reactstrap';
import styled, { css } from 'styled-components';

import StreamField from 'components/common/StreamField';
import { GetPlanPageGeneralQuery } from 'common/__generated__/graphql';
import CategoryPageStreamField, {
checkAttributeHasValueByType,
} from 'components/common/CategoryPageStreamField';

type GeneralPlanPage = NonNullable<GetPlanPageGeneralQuery['planPage']>;

type CategoryPage = { __typename: 'CategoryPage' } & GeneralPlanPage;

const MainContent = styled.div``;

const AsideContent = styled.div`
position: sticky;
top: ${({ theme }) => theme.spaces.s200};
flex: 0 1 320px;
background-color: ${({ theme }) => theme.themeColors.white};
border-radius: ${({ theme }) => theme.cardBorderRadius};
padding: ${({ theme }) => `${theme.spaces.s200} ${theme.spaces.s100}`};
display: flex;
flex-direction: column;
align-items: stretch;
`;

const columnLayout = css`
display: flex;
justify-content: center;
align-items: flex-start;
padding: ${({ theme }) => `0 ${theme.spaces.s200} ${theme.spaces.s200}`};
gap: ${({ theme }) => theme.spaces.s300};

${MainContent} {
flex: 0 2 ${({ theme }) => theme.breakpointMd};
padding: 0 ${({ theme }) => theme.spaces.s100};
background-color: ${({ theme }) => theme.themeColors.white};
border-radius: ${({ theme }) => theme.cardBorderRadius};
}

@media (max-width: ${({ theme }) => theme.breakpointLg}) {
flex-direction: column-reverse;
justify-content: flex-start;
align-items: stretch;

${MainContent}, ${AsideContent} {
position: relative;
top: 0;
flex: 1 0 auto;
width: 100%;
max-width: ${({ theme }) => theme.breakpointMd};
margin: 0 auto;
}
}

@media (max-width: ${({ theme }) => theme.breakpointMd}) {
gap: ${({ theme }) => theme.spaces.s150};
}

@media (max-width: ${({ theme }) => theme.breakpointSm}) {
padding: ${({ theme }) => `0 ${theme.spaces.s050} ${theme.spaces.s050}`};
}
`;

const ContentArea = styled.div<{
$columnLayout: boolean;
$backgroundColor?: string;
}>`
${({ $columnLayout }) => $columnLayout && columnLayout};
background-color: ${({ $backgroundColor }) => $backgroundColor};
`;

const CategoryPageContent = ({
page,
pageSectionColor,
}: {
page: CategoryPage;
pageSectionColor: string;
}) => {
const hasMainContentTemplate = !!page.layout?.layoutMainBottom;
const hasAsideTemplate = !!page.layout?.layoutAside;
const hasAside =
hasAsideTemplate &&
page.layout?.layoutAside?.some((block) =>
checkAttributeHasValueByType(block.attributeType.identifier, page)
);

return (
<ContentArea
$columnLayout={hasAside}
$backgroundColor={hasAside ? pageSectionColor : undefined}
>
<MainContent>
{hasMainContentTemplate
? page.layout?.layoutMainBottom?.map((block, i) => (
<CategoryPageStreamField
key={i}
page={page}
block={block}
hasAsideColumn={hasAside}
/>
))
: page.body && (
<StreamField
page={page}
blocks={page.body}
color={pageSectionColor}
/>
)}
</MainContent>

{hasAside && (
<AsideContent>
<Container>
{page.layout?.layoutAside?.map((block, i) => (
<Row key={i}>
<CategoryPageStreamField
page={page}
block={block}
context="aside"
columnProps={{ md: 12 }}
/>
</Row>
))}
</Container>
</AsideContent>
)}
</ContentArea>
);
};

export default CategoryPageContent;
35 changes: 19 additions & 16 deletions components/common/AttributesBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type AttributeProps = {
vertical: boolean;
};

const Attributes = styled.div<AttributeProps>`
export const Attributes = styled.div<AttributeProps>`
${(props) =>
props.vertical &&
css`
Expand All @@ -38,6 +38,24 @@ const AttributeItem = styled(Col)`
display: block;
`;

export function attributeHasValue(
attribute: AttributesBlockProps['attributes'][0]
) {
const { __typename } = attribute;

if (__typename === 'AttributeChoice') {
return !!(attribute.choice || attribute.text);
} else if (
__typename === 'AttributeText' ||
__typename === 'AttributeRichText'
) {
return !!attribute.value;
} else if (__typename === 'AttributeCategoryChoice') {
return !!attribute.categories.length;
}
return true;
}

type AttributeContentProps = {
attribute: AttributesBlockAttributeFragment;
attributeType: AttributesBlockAttributeTypeFragment;
Expand Down Expand Up @@ -78,21 +96,6 @@ function AttributesBlock(props: AttributesBlockProps) {
typesById = new Map(types.map((type) => [type.id, type]));
}

function attributeHasValue(attribute: AttributesBlockProps['attributes'][0]) {
const { __typename } = attribute;

if (__typename === 'AttributeChoice') {
return !!(attribute.choice || attribute.text);
} else if (
__typename === 'AttributeText' ||
__typename === 'AttributeRichText'
) {
return !!attribute.value;
} else if (__typename === 'AttributeCategoryChoice') {
return !!attribute.categories.length;
}
return true;
}
const attributesWithValue = attributes.filter(attributeHasValue);

return (
Expand Down
Loading