Skip to content

Commit

Permalink
Merge pull request #2545 from bcgov/feature/DESENG-632-engagement-dyn…
Browse files Browse the repository at this point in the history
…amic-pages

[To Main] DESENG-632: Add engagement content tabs
  • Loading branch information
NatSquared authored Jun 20, 2024
2 parents 502fece + ce6a068 commit 18b019d
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 10 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
## June 19, 2024

- **Feature** Add a new tabbed content view to the new engagement page [🎟️ DESENG-632](https://apps.itsm.gov.bc.ca/jira/browse/DESENG-632)
- Added a new tabbed content view to the new engagement page
- Tabs are pulled from the engagement's summary and custom content sections (to be revisited when redoing authoring flow)

## June 18, 2024

- **Feature** Add language picker UI [🎟️ DESENG-623](https://apps.itsm.gov.bc.ca/jira/browse/DESENG-623)

## June 17, 2024
Expand Down
167 changes: 167 additions & 0 deletions met-web/src/components/engagement/new/view/EngagementContentTabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import React, { Suspense, SyntheticEvent, useCallback, useState } from 'react';
import { Tab, Skeleton, Box } from '@mui/material';
import { TabContext, TabList, TabPanel } from '@mui/lab';
import { Await, useLoaderData } from 'react-router-dom';
import { EngagementContent } from 'models/engagementContent';
import { EngagementSummaryContent } from 'models/engagementSummaryContent';
import { Editor } from 'react-draft-wysiwyg';
import { getEditorStateFromRaw } from 'components/common/RichTextEditor/utils';
import { Header2 } from 'components/common/Typography';
import { colors } from 'components/common';

export const EngagementContentTabs = () => {
const { content, contentSummary } = useLoaderData() as {
content: Promise<EngagementContent[]>;
contentSummary: Promise<EngagementSummaryContent[][]>;
};
const [selectedTab, setSelectedTab] = useState('0');
const handleChange = (event: SyntheticEvent<Element, Event>, newValue: string) => {
setSelectedTab(newValue);
};

const panelContents = Promise.all([content, contentSummary]);

const tabListRef = useCallback((node: HTMLButtonElement) => {
if (!node) return;
const scroller = node.getElementsByClassName('MuiTabs-scroller')[0];
scroller.addEventListener('scroll', () => checkFade(node)); // check when scrolling
const resizeObserver = new ResizeObserver(() => checkFade(node));
resizeObserver.observe(scroller); // check when window resizes
checkFade(node); // initial check when attaching the ref
}, []);

const checkFade = (node: HTMLButtonElement) => {
if (!node) return;
const scroller = node.getElementsByClassName('MuiTabs-scroller')[0];
const scrollPosition = scroller.scrollLeft; // distance from left edge
const maxScroll = scroller.scrollWidth - scroller.clientWidth; // distance from right edge
const fadeMargin = 64; // pixels
if (maxScroll - scrollPosition < fadeMargin) {
node.classList.remove('fade-right');
} else {
node.classList.add('fade-right');
}
};

return (
<section id="content-tabs" aria-label="Engagement content tabs">
<Box
sx={{
padding: { xs: '0 16px 24px 16px', md: '0 5vw 40px 5vw', lg: '0 156px 40px 156px' },
marginTop: '-32px',
position: 'relative',
zIndex: 10,
}}
>
<TabContext value={selectedTab}>
<Box sx={{}}>
<Suspense fallback={<Skeleton variant="rectangular" sx={{ width: '300px', height: '81px' }} />}>
<Await resolve={content}>
{(resolvedContent: EngagementContent[]) => (
<TabList
ref={tabListRef}
onChange={handleChange}
variant="scrollable"
scrollButtons={false}
TabIndicatorProps={{
sx: { marginBottom: '16px' },
}}
sx={{
width: {
xs: 'calc(100% + 16px)',
md: 'calc(100% + 5vw)',
lg: 'calc(100% + 156px)',
},
'&.fade-right::after': {
// fade out the right edge of the tab list
content: '""',
display: 'block',
position: 'absolute',
top: 0,
right: 0,
width: '48px',
height: '100%',
background:
'linear-gradient(to left, rgba(255,255,255,1) 0%, rgba(255,255,255,0.8) 5%, rgba(255,255,255,0.8) 50%, rgba(255,255,255,0) 100%)',
pointerEvents: 'none', //allow clicking on faded tabs
},
'& .MuiTabs-indicator': {
display: 'flex',
justifyContent: 'center',
height: '6px',
backgroundColor: colors.surface.blue[90],
},
'& .MuiTabs-scroller': {
width: 'max-content',
paddingBottom: '16px',
},
'& .MuiTabs-flexContainer': {
justifyContent: 'flex-start',
width: 'max-content',
},
}}
>
<Box sx={{ width: '16px', background: 'white' }}></Box>
{resolvedContent.map((section, index) => {
return (
<Tab
sx={{
background: 'white',
color: colors.type.regular.primary,
fontSize: '14px',
fontWeight: 'normal',
padding: '24px 8px',
paddingLeft: '8px',
'&.Mui-selected': {
fontWeight: 'bold',
color: colors.surface.blue[90],
},
}}
key={section.id}
label={section.title}
value={index.toString()}
/>
);
})}
<Box
sx={{
background: 'white',
width: '24px',
borderRadius: '0px 24px 0px 0px',
marginRight: { xs: '16px', md: '5vw', lg: '156px' },
}}
></Box>
</TabList>
)}
</Await>
</Suspense>
</Box>
<Suspense fallback={<Skeleton variant="rectangular" sx={{ width: '100%', height: '120px' }} />}>
<Await resolve={panelContents}>
{([content, contentSummary]: [
content: EngagementContent[],
contentSummary: EngagementSummaryContent[][],
]) =>
contentSummary.map((summary, index) => (
<TabPanel key={summary[0].id} value={index.toString()} sx={{ padding: '24px 0px' }}>
<Header2 decorated weight="thin">
{content[index].title}
</Header2>
{summary.map((content, index) => (
<Editor
key={content.id}
editorState={getEditorStateFromRaw(content.rich_content)}
readOnly={true}
toolbarHidden
/>
))}
</TabPanel>
))
}
</Await>
</Suspense>
</TabContext>
</Box>
</section>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const EngagementDescription = ({ engagement }: { engagement: Engagement }
background: colors.surface.blue[90],
color: colors.surface.white,
borderRadius: '0px 24px 0px 0px' /* upper right corner */,
padding: { xs: '43px 16px 75px 16px', md: '32px 5vw 88px 5vw', lg: '32px 156px 88px 156px' },
padding: { xs: '43px 16px 24px 16px', md: '32px 5vw 40px 5vw', lg: '32px 156px 40px 156px' },
marginTop: '-56px',
zIndex: 10,
position: 'relative',
Expand All @@ -41,7 +41,7 @@ export const EngagementDescription = ({ engagement }: { engagement: Engagement }
marginBottom: { xs: '24px', md: '48px' },
}}
>
<Grid item component={Link} to={'/'} alignItems="center" display="flex">
<Grid item container component={Link} to={'/'} alignItems="center" display="flex">
<FontAwesomeIcon
icon={faArrowLeftLong}
color={colors.surface.white}
Expand All @@ -60,6 +60,7 @@ export const EngagementDescription = ({ engagement }: { engagement: Engagement }
display: 'flex',
flexDirection: 'column',
minHeight: '120px',
marginBottom: '48px',
}}
>
<Header2 decorated weight="thin" sx={{ color: colors.surface.white }} id="description-header">
Expand Down Expand Up @@ -88,6 +89,7 @@ export const EngagementDescription = ({ engagement }: { engagement: Engagement }
width: { xs: '100%', md: '47.5%' },
display: 'flex',
minHeight: '360px',
marginBottom: '48px',
}}
>
<Suspense fallback={<Skeleton variant="rectangular" sx={{ width: '100%', height: '360px' }} />}>
Expand Down
28 changes: 27 additions & 1 deletion met-web/src/components/engagement/new/view/EngagementLoader.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
import { EngagementCustomContent } from 'models/engagementCustomContent';
import { EngagementSummaryContent } from 'models/engagementSummaryContent';
import { Params, defer } from 'react-router-dom';
import { getEngagementContent } from 'services/engagementContentService';
import { getCustomContent } from 'services/engagementCustomService';
import { getEngagement } from 'services/engagementService';
import { getEngagementIdBySlug } from 'services/engagementSlugService';
import { getSummaryContent } from 'services/engagementSummaryService';
import { getWidgets } from 'services/widgetService';

const castCustomContentToSummaryContent = (customContent: EngagementCustomContent[]): EngagementSummaryContent[] => {
return customContent.map((custom) => ({
id: custom.id,
content: custom.custom_text_content,
rich_content: custom.custom_json_content,
engagement_id: custom.engagement_id,
engagement_content_id: custom.engagement_content_id,
}));
};

export const engagementLoader = async ({ params }: { params: Params<string> }) => {
const { slug } = params;
const engagement = getEngagementIdBySlug(slug ?? '').then((response) => getEngagement(response.engagement_id));
const widgets = engagement.then((response) => getWidgets(response.id));
return defer({ engagement, slug, widgets });
const content = engagement.then((response) => getEngagementContent(response.id));
const contentSummary = content.then((response) =>
Promise.all(
response.map((content) => {
return content.content_type === 'Summary'
? getSummaryContent(content.id)
: getCustomContent(content.id).then(castCustomContentToSummaryContent);
}),
),
);

return defer({ engagement, slug, widgets, content, contentSummary });
};
2 changes: 2 additions & 0 deletions met-web/src/components/engagement/new/view/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Engagement } from 'models/engagement';
import { Skeleton } from '@mui/material';
import { EngagementHero } from './EngagementHero';
import { EngagementDescription } from './EngagementDescription';
import { EngagementContentTabs } from './EngagementContentTabs';

export const ViewEngagement = () => {
const { engagement } = useLoaderData() as { engagement: Engagement };
Expand Down Expand Up @@ -32,6 +33,7 @@ export const ViewEngagement = () => {
{(resolvedEngagement: Engagement) => <EngagementDescription engagement={resolvedEngagement} />}
</Await>
</Suspense>
<EngagementContentTabs />
</main>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,18 @@ const WhoIsListeningWidget = ({ widget }: WhoIsListeningWidgetProps) => {

if (isLoading) {
return (
<MetPaper elevation={1} sx={{ padding: '1em' }}>
<Grid container justifyContent="flex-start" spacing={3}>
<MetPaper elevation={1} sx={{ padding: '1em', width: '100%' }}>
<Grid container justifyContent="flex-start" spacing={3} width="100%">
<Grid item xs={12}>
<Header2>
<Skeleton variant="rectangular" />
</Header2>
<Skeleton>
<Header2>Who is Listening</Header2>
</Skeleton>
</Grid>
<Grid item xs={12}>
<Skeleton variant="rectangular" height="10em" />
<Skeleton variant="rectangular" height="10em" width="100%" />
</Grid>
<Grid item xs={12}>
<Skeleton variant="rectangular" height="10em" />
<Skeleton variant="rectangular" height="10em" width="100%" />
</Grid>
</Grid>
</MetPaper>
Expand Down

0 comments on commit 18b019d

Please sign in to comment.