diff --git a/example-next/cms/sections/CustomSection.js b/example-next/cms/sections/CustomSection.js new file mode 100644 index 000000000..420017638 --- /dev/null +++ b/example-next/cms/sections/CustomSection.js @@ -0,0 +1,87 @@ +import {Grid, SimpleGrid, Stack} from "@mantine/core"; +import React, {useRef} from "react"; +import {Viz} from "@datawheel/canon-next"; + +/** + * CMS custom section + */ +export default function CustomSection({ + slug, + heading, + hideOptions, + title, + paragraphs, + configOverride, + loading, + filters, + resetButton, + stats, + sources, + visualizations, + vizHeadingLevel, + updateSource, + onSetVariables +}) { + const section = useRef(null); + + return ( + + {/* sidebar */} + + + {heading} + {filters} + {stats} + {paragraphs} + {sources} + {resetButton} + + + + {/* caption */} + {visualizations.length + ? 1 ? " cp-multicolumn-default-section-figure" : "" + }${ + visualizations.filter( + viz => viz.logic_simple && viz.logic_simple.type === "Graphic" + ).length ? " cp-graphic-viz-grid" : "" + }`} + > + = 2 ? 2 : 1} + ]} + > + {visualizations.map(visualization => + + )} + + + : ""} + + ); +} diff --git a/example-next/cms/sections/Default.js b/example-next/cms/sections/Default.js new file mode 100644 index 000000000..fa4cf5ce1 --- /dev/null +++ b/example-next/cms/sections/Default.js @@ -0,0 +1,87 @@ +import {Grid, SimpleGrid, Stack, Title} from "@mantine/core"; +import React, {useRef} from "react"; +import {Viz} from "@datawheel/canon-next"; + +/** + * CMS custom section + */ +export default function Default({ + slug, + heading, + hideOptions, + title, + paragraphs, + configOverride, + loading, + filters, + resetButton, + stats, + sources, + visualizations, + vizHeadingLevel, + updateSource, + onSetVariables +}) { + const section = useRef(null); + + return ( + + {/* sidebar */} + + + {heading} + {filters} + {stats} + {paragraphs} + {sources} + {resetButton} + + + + {/* caption */} + {visualizations.length + ? 1 ? " cp-multicolumn-default-section-figure" : "" + }${ + visualizations.filter( + viz => viz.logic_simple && viz.logic_simple.type === "Graphic" + ).length ? " cp-graphic-viz-grid" : "" + }`} + > + = 2 ? 2 : 1} + ]} + > + {visualizations.map(visualization => + + )} + + + : ""} + + ); +} diff --git a/example-next/cms/sections/index.js b/example-next/cms/sections/index.js new file mode 100644 index 000000000..19ca0681f --- /dev/null +++ b/example-next/cms/sections/index.js @@ -0,0 +1,6 @@ +import CustomSection from "./CustomSection"; +import Default from "./Default"; +export default { + CustomSection, + Default +}; diff --git a/example-next/jsconfig.json b/example-next/jsconfig.json index 9c3338335..bf2988226 100644 --- a/example-next/jsconfig.json +++ b/example-next/jsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "@/*": ["./*"] + "@/*": ["./*"], } } } diff --git a/example-next/next.config.js b/example-next/next.config.js index ae2e3ae87..e90c0baa5 100644 --- a/example-next/next.config.js +++ b/example-next/next.config.js @@ -1,7 +1,14 @@ const {i18n} = require("./next-i18next.config"); +const rootDir = process.cwd(); +const path = require("path"); + const nextConfig = { i18n, - reactStrictMode: true + reactStrictMode: true, + webpack(config, options) { + config.resolve.alias.CustomSections = path.join(__dirname, "cms/sections"); + return config; + } }; module.exports = nextConfig; diff --git a/example-next/pages/index.js b/example-next/pages/index.js index a5bb3f5df..3f91c1413 100644 --- a/example-next/pages/index.js +++ b/example-next/pages/index.js @@ -22,7 +22,7 @@ export default function Home() { handlers.close()} fullScreen> - + diff --git a/example-next/pages/profile/[...members].js b/example-next/pages/profile/[...members].js index 4b44ba4dc..71410cdc5 100644 --- a/example-next/pages/profile/[...members].js +++ b/example-next/pages/profile/[...members].js @@ -11,6 +11,7 @@ import {useRouter} from "next/router"; import {serverSideTranslations} from "next-i18next/serverSideTranslations"; import {Profile, NonIdealState, cmsDefaultPaths, cmsDefaultProps} from "@datawheel/canon-next"; + function ProfilePage({profile, formatters}) { const router = useRouter(); const {t} = useTranslation("profile"); diff --git a/example-next/pages/test-viz/index.js b/example-next/pages/test-viz/index.js new file mode 100644 index 000000000..4edb6a03d --- /dev/null +++ b/example-next/pages/test-viz/index.js @@ -0,0 +1,7 @@ +import {Viz} from "@datawheel/canon-next"; + +/** */ +export default function Page() { + console.log(Viz); + return
Just testing
; +} diff --git a/package-lock.json b/package-lock.json index 2882e0c36..f66d245af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7824,7 +7824,7 @@ }, "packages/next": { "name": "@datawheel/canon-next", - "version": "0.1.3", + "version": "0.1.4", "license": "GPL-3.0-or-later", "dependencies": { "@mantine/core": "^5.6.3", diff --git a/packages/next/cms/components/Viz/Viz.js b/packages/next/cms/components/Viz/Viz.js index 0d039fd47..d2a88665d 100644 --- a/packages/next/cms/components/Viz/Viz.js +++ b/packages/next/cms/components/Viz/Viz.js @@ -10,7 +10,8 @@ import {useRouter} from "next/router.js"; import {Title} from "@mantine/core"; import {useViewportSize} from "@mantine/hooks"; import * as d3plus from "d3plus-react"; -import {Options, ProfileContext} from "../../../index"; +import Options from "./Options" +import ProfileContext from "../ProfileContext"; import propify from "../../utils/d3plusPropify"; import HTML from "./HTML"; // User must define custom sections in app/cms/sections, and export them from an index.js in that folder. @@ -120,7 +121,7 @@ function Viz(props) {
{title && showTitle - ? <Text + component="li" key="filters-all" fw={!filterProfiles ? 700 : 400} sx={{"cursor": "pointer", "&:hover": {textDecoration: "underline"}}} @@ -363,7 +364,9 @@ function ProfileSearch({ return ( <Text component="li" + id={`filters-${profileIds.join("-")}`} sx={{"cursor": "pointer", "&:hover": {textDecoration: "underline"}}} + className={`cms-profilesearch-filters-profile ${profileIds.join(",") === filterProfiles ? "active" : ""}`} key={`filters-${profileIds.join("-")}`} fw={profileIds.join(",") === filterProfiles ? 700 : 400} onClick={() => setFilterProfiles(profileIds.join(","))} @@ -385,13 +388,13 @@ function ProfileSearch({ > { d.levels ? <Anchor - // className={`cms-profilesearch-filters-dimension${filterLevels && filterLevels.includes(d.levels.join(",")) ? " active" : ""}`} + className={`cms-profilesearch-filters-dimension${filterLevels && filterLevels.includes(d.levels.join(",")) ? " active" : ""}`} onClick={() => onFilterLevels(false)} dangerouslySetInnerHTML={{__html: d.title}} /> : <Anchor - // className={`cms-profilesearch-filters-dimension${filterCubes && filterCubes.includes(d.cubes.join(",")) ? " active" : ""}`} + className={`cms-profilesearch-filters-dimension${filterCubes && filterCubes.includes(d.cubes.join(",")) ? " active" : ""}`} onClick={() => onFilterLevels(false)} dangerouslySetInnerHTML={{__html: d.title}} /> @@ -400,7 +403,7 @@ function ProfileSearch({ ? d.sortedLevels.map(l => <Anchor key={`filters-level-${l}`} - // className={`cms-profilesearch-filters-level${filterLevels && !filterLevels.includes(d.levels.join(",")) && filterLevels.includes(l) ? " active" : ""}`} + className={`cms-profilesearch-filters-level${filterLevels && !filterLevels.includes(d.levels.join(",")) && filterLevels.includes(l) ? " active" : ""}`} onClick={() => onFilterLevels(l)} dangerouslySetInnerHTML={{__html: filterHierarchyTitle(l, activeProfile[0])}} /> diff --git a/packages/next/cms/components/sections/Hero.js b/packages/next/cms/components/sections/Hero.js index 4e327919f..59c3fcae9 100644 --- a/packages/next/cms/components/sections/Hero.js +++ b/packages/next/cms/components/sections/Hero.js @@ -7,10 +7,10 @@ import { import { IconChevronDown, IconChevronUp } from "@tabler/icons-react"; -// eslint-disable-next-line import/no-cycle -import { - ProfileContext, SourceGroup, StatGroup, Viz -} from "../../../index"; +import ProfileContext from "../ProfileContext"; +import SourceGroup from "../Viz/SourceGroup"; +import StatGroup from "../Viz/StatGroup"; +import Viz from "../Viz/Viz"; import stripP from "../../utils/formatters/stripP"; // import {strip} from "d3plus-text"; @@ -181,7 +181,7 @@ function Hero({ </Flex> {/* display image credits, and images */} - {profile.images.length && + {profile.images.length ? <> {/* credits */} {type !== "story" && hasAuthor && @@ -268,6 +268,14 @@ function Hero({ zIndex={1} /> </> + : + + <Overlay + opacity={0.7} + blur={2} + zIndex={1} + /> + } <Modal diff --git a/packages/next/cms/components/sections/Section.js b/packages/next/cms/components/sections/Section.js index 92ad4d874..f12e23f57 100644 --- a/packages/next/cms/components/sections/Section.js +++ b/packages/next/cms/components/sections/Section.js @@ -3,9 +3,9 @@ import Link from "next/link.js"; import React, {useState, useRef, useContext} from "react"; import {nest} from "d3-collection"; -// import * as CustomSections from "CustomSections"; +import CustomSections from "CustomSections"; import { - Text, Flex, Anchor, Button, + Text, Flex, Anchor, Button } from "@mantine/core"; import {useWindowEvent} from "@mantine/hooks"; // eslint-disable-next-line import/extensions @@ -38,8 +38,10 @@ import ProfileContext from "../ProfileContext"; // - On set variables and reset variables // TODO: Custom Sections + +console.log(CustomSections); const sectionTypes = { - Default, Grouping, SubGrouping, MultiColumn, SingleColumn, Tabs, + Default, Grouping, SubGrouping, MultiColumn, SingleColumn, Tabs, ...CustomSections }; function Section({ @@ -73,7 +75,7 @@ function Section({ // eslint-disable-next-line react/destructuring-assignment const initialVariables = rest.initialVariables || ctx.initialVariables || {}; const changedVariables = {}; - Object.keys(newVariables).forEach((key) => { + Object.keys(newVariables).forEach(key => { changedVariables[key] = initialVariables[key]; }); setChangedVariables(changedVariables); @@ -99,7 +101,8 @@ function Section({ if (screenTop !== containerTop) { if (containerTop < screenTop && !isStickyIE) { setIsStickyIE(true); - } else if (containerTop > screenTop && isStickyIE) { + } + else if (containerTop > screenTop && isStickyIE) { setIsStickyIE(false); } } @@ -108,15 +111,18 @@ function Section({ useWindowEvent("scroll", scrollHandler); - const updateSource = (newSources) => { - if (!newSources) { setSources([]); } else { - setSources((oldSources) => { + const updateSource = newSources => { + if (!newSources) { + setSources([]); + } + else { + setSources(oldSources => { const addSources = newSources - .map((s) => s.annotations) + .map(s => s.annotations) // filter out new sources that already are on the sources state variable. - .filter((source) => ( - source && source.source_name && !oldSources.find((s) => s.source_name === source.source_name) - )); + .filter(source => + source && source.source_name && !oldSources.find(s => s.source_name === source.source_name) + ); return [...oldSources, ...addSources]; }); } @@ -124,16 +130,17 @@ function Section({ const layout = contents.type; const layoutClass = `cp-${toKebabCase(layout)}-section`; + const Layout = contents.position === "sticky" ? Default : sectionTypes[layout] || Default; const showAnchor = !(isModal || hideAnchor); const { - slug, title, descriptions, subtitles, visualizations, + slug, title, descriptions, subtitles, visualizations } = contents; const selectors = contents.selectors || []; - const filters = selectors.map((selector) => ( + const filters = selectors.map(selector => <Selector key={selector.id} // eslint-disable-next-line react/jsx-props-no-spreading @@ -141,45 +148,45 @@ function Section({ loading={loading} fontSize="xxs" /> - )); - - const mainTitle = ( - title - && ( - <Flex - className={`cp-section-heading-wrapper ${layoutClass}-heading-wrapper`} - wrap="nowrap" - justify="center" - direction="row-reverse" - align="center" - gap="xs" - > - <Parse - El={headingLevel} - id={slug} - className={`cp-section-heading ${layoutClass}-heading${ - layout !== "Hero" && showAnchor - ? " cp-section-anchored-heading" : ""}`} - tabIndex="0" + ); + + const mainTitle = + title && + + <Flex + className={`cp-section-heading-wrapper ${layoutClass}-heading-wrapper`} + wrap="nowrap" + justify="center" + direction="row-reverse" + align="center" + gap="xs" > - {title} - </Parse> - {showAnchor - && ( - <Link href={`#${slug}`} className={`cp-section-heading-anchor ${layoutClass}-heading-anchor`} passHref legacyBehavior> - <Anchor> + <Parse + El={headingLevel} + id={slug} + className={`cp-section-heading ${layoutClass}-heading${ + layout !== "Hero" && showAnchor + ? " cp-section-anchored-heading" : ""}`} + tabIndex="0" + > + {title} + </Parse> + {showAnchor && + + <Link href={`#${slug}`} className={`cp-section-heading-anchor ${layoutClass}-heading-anchor`} passHref legacyBehavior> + <Anchor> # - <Text display="none" className="u-visually-hidden" span>permalink to section</Text> - </Anchor> - </Link> - )} - </Flex> - ) + <Text display="none" className="u-visually-hidden" span>permalink to section</Text> + </Anchor> + </Link> + } + </Flex> + - ); + ; - const subTitle = ( - contents.position !== "sticky" && subtitles.map((content, i) => ( + const subTitle = + contents.position !== "sticky" && subtitles.map((content, i) => <Parse className={`cp-section-subhead display ${layoutClass}-subhead`} // eslint-disable-next-line react/no-array-index-key @@ -187,54 +194,54 @@ function Section({ > {content.subtitle} </Parse> - )) - ); - const heading = ( + ) + ; + const heading = <> {mainTitle} {subTitle} </> - ); + ; let statContent; const {stats} = contents; if (contents.position !== "sticky") { - const statGroups = nest().key((d) => d.title).entries(stats); + const statGroups = nest().key(d => d.title).entries(stats); if (stats.length > 0) { - statContent = ( + statContent = <Flex sx={{ "& > *": { - flex: "1 0 50%", - }, + flex: "1 0 50%" + } }} className={`cp-stat-group-wrapper${stats.length === 1 ? " single-stat" : ""}`} wrap="wrap" > {statGroups.map(({key, values}) => <StatGroup key={key} title={key} stats={values} />)} </Flex> - ); + ; } } let paragraphs; if (descriptions.length && contents.position !== "sticky") { - paragraphs = descriptions.map((content, i) => ( + paragraphs = descriptions.map((content, i) => <Parse className={`cp-section-paragraph ${layoutClass}-paragraph`} key={`${content.description}-paragraph-${i}`} > {content.description} </Parse> - )); + ); } const sourceContent = <SourceGroup sources={sources} />; - const resetButton = showReset - && ( + const resetButton = showReset && + <Button onClick={resetVariables} className={`cp-var-reset-button ${layoutClass}-var-reset-button`} @@ -245,7 +252,7 @@ function Section({ > {t("CMS.Section.Reset visualizations")} </Button> - ); + ; const componentProps = { slug, @@ -265,7 +272,7 @@ function Section({ contents, // these were set as context child props. Not possible with new Context API updateSource, - onSetVariables, + onSetVariables }; return ( @@ -285,12 +292,12 @@ function Section({ <Layout {...componentProps} /> {/* in IE, create empty div set to the height of the stuck element */} - {isStickyIE && ( + {isStickyIE && <> <div className="ie-sticky-spacer" style={{height}} /> <div className="ie-sticky-section-color-fixer" /> </> - )} + } </section> ); } diff --git a/packages/next/index.ts b/packages/next/index.ts index b8ab28fca..08c5df2ec 100644 --- a/packages/next/index.ts +++ b/packages/next/index.ts @@ -8,11 +8,13 @@ export {default as ProfileSearch} from "./cms/components/fields/ProfileSearch.js export {default as ProfileTile} from "./cms/components/fields/ProfileTile.js"; export {default as Hero} from "./cms/components/sections/Hero.js"; -export {default as Stat} from "./cms/components/sections/components/Stat.js"; + +// Elements to be used by Custom Sections +// export {default as PDFButton} from "./components/sections/components/PDFButton.jsx"; +export {default as Selector} from "./cms/components/sections/components/Selector.js"; +export {default as Stat} from "./cms/components/sections/components/Stat.js"; export {default as Viz} from "./cms/components/Viz/Viz.js"; -export {default as Options} from "./cms/components/Viz/Options.js"; -export {default as SourceGroup} from "./cms/components/Viz/SourceGroup.js"; export {default as StatGroup} from "./cms/components/Viz/StatGroup.js"; export {default as cmsDefaultProps} from "./ssg/cmsDefaultProps.js"; diff --git a/packages/next/package.json b/packages/next/package.json index 702b8c22c..ff98686d9 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "@datawheel/canon-next", - "version": "0.1.3", + "version": "0.1.4", "description": "Nextjs components for sites migrating away from canon-core.", "main": "./dist/index.js", "exports": { diff --git a/packages/next/tsup.config.js b/packages/next/tsup.config.js index 5d493d5bb..d950e7b14 100644 --- a/packages/next/tsup.config.js +++ b/packages/next/tsup.config.js @@ -13,4 +13,5 @@ export default defineConfig(options => ({ shims: true, splitting: false, treeshake: true, + external: ["CustomSections"], }));