@openeventkit/event-site 2.0.110 → 2.0.112

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/gatsby-browser.js +1 -1
  2. package/{gatsby-config.js → gatsby-config.mjs} +56 -20
  3. package/gatsby-node.js +145 -92
  4. package/gatsby-ssr.js +2 -14
  5. package/package.json +30 -28
  6. package/src/cms/cms-utils.js +15 -8
  7. package/src/cms/cms.js +1 -1
  8. package/src/cms/config/collections/configurationsCollection/siteSettings/index.js +25 -5
  9. package/src/cms/config/collections/configurationsCollection/siteSettings/typeDefs.js +6 -0
  10. package/src/cms/config/collections/contentPagesCollection/typeDefs.js +8 -1
  11. package/src/cms/config/collections/defaultPagesCollection/marketingPage/index.js +5 -5
  12. package/src/cms/config/collections/defaultPagesCollection/marketingPage/typeDefs.js +4 -4
  13. package/src/cms/preview-templates/ContentPagePreview.js +41 -13
  14. package/src/cms/widgets/IdentityProviderParamControl.js +1 -1
  15. package/src/components/DisqusComponent.js +1 -1
  16. package/src/components/LiteScheduleComponent.js +9 -11
  17. package/src/components/ResponsiveImage.js +30 -0
  18. package/src/components/summit-my-orders-tickets/components/TicketPopup/TicketPopupEditDetailsForm/TicketPopupEditDetailsForm.js +4 -4
  19. package/src/content/site-settings/index.json +6 -1
  20. package/src/pages/index.js +6 -6
  21. package/src/pages/maintenance.js +18 -0
  22. package/src/styles/colors.scss +4 -0
  23. package/src/styles/marketing.module.scss +6 -4
  24. package/src/styles/style.scss +0 -26
  25. package/src/templates/content-page/index.js +74 -0
  26. package/src/templates/content-page/shortcodes.js +17 -0
  27. package/src/templates/content-page/template.js +21 -0
  28. package/src/templates/marketing-page-template.js +66 -38
  29. package/src/utils/buildPolyfills.js +18 -0
  30. package/src/utils/filePath.js +18 -14
  31. package/src/utils/useSiteSettings.js +4 -0
  32. package/src/components/Content.js +0 -42
  33. package/src/content/maintenance.json +0 -5
  34. package/src/pages/maintenance.md +0 -3
  35. package/src/templates/content-page.js +0 -92
  36. package/src/templates/maintenance-page.js +0 -40
  37. package/static/admin/admin.css +0 -3
@@ -121,9 +121,9 @@ const siteSettings = {
121
121
  name: "allowClick",
122
122
  required: false,
123
123
  default: true
124
- }),
124
+ })
125
125
  ]
126
- }),
126
+ })
127
127
  ]
128
128
  }),
129
129
  objectField({
@@ -172,8 +172,8 @@ const siteSettings = {
172
172
  required: false,
173
173
  options: getFontFormatOptions()
174
174
  })
175
- ],
176
- }),
175
+ ]
176
+ })
177
177
  ]
178
178
  }),
179
179
  objectField({
@@ -194,7 +194,7 @@ const siteSettings = {
194
194
  label: "Logo Alt",
195
195
  name: "idpLogoAlt",
196
196
  required: false
197
- }),
197
+ })
198
198
  ]
199
199
  }),
200
200
  listField({
@@ -237,6 +237,26 @@ const siteSettings = {
237
237
  required: true
238
238
  })
239
239
  ]
240
+ }),
241
+ objectField({
242
+ label: "Maintenance Mode",
243
+ name: "maintenanceMode",
244
+ fields: [
245
+ booleanField({
246
+ label: "Enabled",
247
+ name: "enabled",
248
+ required: false,
249
+ default: false
250
+ }),
251
+ stringField({
252
+ label: "Title",
253
+ name: "title"
254
+ }),
255
+ stringField({
256
+ label: "Subtitle",
257
+ name: "subtitle"
258
+ })
259
+ ]
240
260
  })
241
261
  ]
242
262
  };
@@ -34,11 +34,17 @@ module.exports = `
34
34
  providerLogo: File @fileByRelativePath
35
35
  providerLogoSize: Float
36
36
  }
37
+ type MaintenanceMode {
38
+ enabled: Boolean
39
+ title: String
40
+ subtitle: String
41
+ }
37
42
  type SiteSettingsJson implements Node {
38
43
  siteMetadata: SiteMetadata
39
44
  favicon: Favicon
40
45
  widgets: Widgets
41
46
  idpLogo: IdpLogo
42
47
  identityProviderButtons: [IdentityProviderButton]
48
+ maintenanceMode: MaintenanceMode
43
49
  }
44
50
  `;
@@ -1,8 +1,15 @@
1
1
 
2
2
  module.exports = `
3
- type MarkdownRemarkFrontmatter {
3
+ type MdxFields {
4
+ slug: String
5
+ }
6
+ type MdxFrontmatter {
4
7
  templateKey: String
5
8
  title: String
6
9
  userRequirement: String
7
10
  }
11
+ type Mdx {
12
+ frontmatter: MdxFrontmatter
13
+ fields: MdxFields
14
+ }
8
15
  `;
@@ -126,8 +126,8 @@ const marketingPage = {
126
126
  name: "widgets",
127
127
  fields: [
128
128
  objectField({
129
- label: "Text",
130
- name: "text",
129
+ label: "Content",
130
+ name: "content",
131
131
  fields: [
132
132
  booleanField({
133
133
  label: "Display",
@@ -135,10 +135,10 @@ const marketingPage = {
135
135
  required: false
136
136
  }),
137
137
  markdownField({
138
- label: "Content",
139
- name: "content",
138
+ label: "Body",
139
+ name: "body",
140
140
  buttons: markdownFieldButtons,
141
- editor_components: []
141
+ editor_components: ["image"]
142
142
  })
143
143
  ]
144
144
  }),
@@ -23,9 +23,9 @@ module.exports = `
23
23
  display: Boolean
24
24
  title: String
25
25
  }
26
- type MarketingPageTextWidget {
26
+ type MarketingPageContentWidget {
27
27
  display: Boolean
28
- content: String
28
+ body: String @resolveImages
29
29
  }
30
30
  type MarketingPageImageWidget {
31
31
  display: Boolean
@@ -33,10 +33,10 @@ module.exports = `
33
33
  image: ImageWithAlt
34
34
  }
35
35
  type MarketingPageWidgets {
36
- text: MarketingPageTextWidget
37
- image: MarketingPageImageWidget
36
+ content: MarketingPageContentWidget
38
37
  schedule: MarketingPageWidget
39
38
  disqus: MarketingPageWidget
39
+ image: MarketingPageImageWidget
40
40
  }
41
41
  type MarketingPageCountdown {
42
42
  display: Boolean
@@ -1,21 +1,49 @@
1
- import React from 'react'
2
- import PropTypes from 'prop-types'
3
- import { ContentPageTemplate } from '../../templates/content-page'
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import Mdx from "@mdx-js/runtime";
4
+ import ContentPageTemplate from "../../templates/content-page/template";
5
+ import shortcodes from "../../templates/content-page/shortcodes";
4
6
 
5
- const ContentPagePreview = ({ entry, getAsset, widgetFor }) => {
7
+ // function to transform content by replacing relative image URLs with absolute ones
8
+ const transformContent = (mdx, getAsset) => {
9
+ // regex to identify Markdown image tags ![alt](url)
10
+ const imageRegex = /!\[([^\]]*)\]\(([^)]+)\)/g;
11
+
12
+ return mdx.replace(imageRegex, (match, alt, url) => {
13
+ // check if the URL is relative (does not start with http:// or https://)
14
+ if (!url.startsWith("http://") && !url.startsWith("https://")) {
15
+ const asset = getAsset(url);
16
+ if (asset && asset.url) {
17
+ return `![${alt}](${asset.url})`;
18
+ }
19
+ }
20
+ return match; // return the original match if it's already an absolute URL
21
+ });
22
+ };
23
+
24
+ // function to render transformed content with Mdx
25
+ const renderContent = (mdx, getAsset) => (
26
+ <Mdx components={shortcodes}>
27
+ {transformContent(mdx, getAsset)}
28
+ </Mdx>
29
+ );
30
+
31
+ const ContentPagePreview = ({ entry, getAsset }) => {
32
+ const title = entry.getIn(["data", "title"]);
33
+ const body = entry.getIn(["data", "body"]);
6
34
  return (
7
35
  <ContentPageTemplate
8
- title={entry.getIn(['data', 'title'])}
9
- content={widgetFor('body')}
36
+ title={title}
37
+ content={renderContent(body, getAsset)}
10
38
  />
11
- )
12
- }
39
+ );
40
+ };
13
41
 
14
42
  ContentPagePreview.propTypes = {
15
43
  entry: PropTypes.shape({
16
- getIn: PropTypes.func,
17
- }),
18
- getAsset: PropTypes.func,
19
- }
44
+ getIn: PropTypes.func.isRequired
45
+ }).isRequired,
46
+ getAsset: PropTypes.func.isRequired
47
+ };
20
48
 
21
- export default ContentPagePreview
49
+ export default ContentPagePreview;
@@ -1,8 +1,8 @@
1
1
  import React, { useState, useEffect } from "react";
2
- import CMS from "netlify-cms-app";
3
2
  import PropTypes from "prop-types";
4
3
  import ImmutablePropTypes from "react-immutable-proptypes";
5
4
  import { List } from "immutable";
5
+ import CMS from "decap-cms-app";
6
6
 
7
7
  const StringControl = CMS.getWidget("string").control;
8
8
  const SelectControl = CMS.getWidget("select").control;
@@ -161,7 +161,7 @@ const DisqusComponent = class extends React.Component {
161
161
  };
162
162
 
163
163
  const { title, style, className, page, skipTo } = this.props;
164
- const sectionClass = className ? className : style ? '' : page === 'marketing-site' ? 'disqus-container-marketing' : 'disqus-container';
164
+ const sectionClass = className ? className : style || page === 'marketing-site' ? '' : 'disqus-container';
165
165
 
166
166
  return (
167
167
  <section aria-labelledby={title ? 'disqus-title' : ''} className={sectionClass} style={style}>
@@ -3,30 +3,28 @@ import * as Sentry from "@sentry/react";
3
3
  import {connect} from "react-redux";
4
4
 
5
5
  // these two libraries are client-side only
6
- import LiteSchedule from 'lite-schedule-widget/dist';
7
- import 'lite-schedule-widget/dist/index.css';
6
+ import LiteSchedule from "lite-schedule-widget/dist";
7
+ import "lite-schedule-widget/dist/index.css";
8
8
  // awesome-bootstrap-checkbox css dependency
9
9
  // https://cdnjs.cloudflare.com/ajax/libs/awesome-bootstrap-checkbox/1.0.2/awesome-bootstrap-checkbox.min.css
10
10
  // injected through HeadComponents
11
11
 
12
- import {addToSchedule, removeFromSchedule} from '../actions/user-actions';
12
+ import {addToSchedule, removeFromSchedule} from "../actions/user-actions";
13
13
 
14
14
  import useMarketingSettings, { MARKETING_SETTINGS_KEYS } from "@utils/useMarketingSettings";
15
15
  import { SentryFallbackFunction } from "./SentryErrorComponent";
16
16
 
17
17
  const LiteScheduleComponent = ({
18
- className,
18
+ className = "schedule-container",
19
19
  userProfile,
20
20
  colorSettings,
21
- page,
22
21
  addToSchedule,
23
22
  removeFromSchedule,
24
23
  schedules,
25
24
  summit,
26
- schedKey = 'schedule-main',
25
+ schedKey = "schedule-main",
27
26
  ...rest
28
27
  }) => {
29
- const wrapperClass = page === 'marketing-site' ? 'schedule-container-marketing' : 'schedule-container';
30
28
  const { getSettingByKey } = useMarketingSettings();
31
29
  const defaultImage = getSettingByKey(MARKETING_SETTINGS_KEYS.schedultDefaultImage);
32
30
  const scheduleState = schedules?.find( s => s.key === schedKey);
@@ -39,10 +37,10 @@ const LiteScheduleComponent = ({
39
37
  userProfile: userProfile,
40
38
  triggerAction: (action, {event}) => {
41
39
  switch (action) {
42
- case 'ADDED_TO_SCHEDULE': {
40
+ case "ADDED_TO_SCHEDULE": {
43
41
  return addToSchedule(event);
44
42
  }
45
- case 'REMOVED_FROM_SCHEDULE': {
43
+ case "REMOVED_FROM_SCHEDULE": {
46
44
  return removeFromSchedule(event);
47
45
  }
48
46
  default: {
@@ -53,8 +51,8 @@ const LiteScheduleComponent = ({
53
51
  };
54
52
 
55
53
  return (
56
- <div className={className || wrapperClass}>
57
- <Sentry.ErrorBoundary fallback={SentryFallbackFunction({componentName: 'Schedule Lite'})}>
54
+ <div className={className}>
55
+ <Sentry.ErrorBoundary fallback={SentryFallbackFunction({componentName: "Schedule Lite"})}>
58
56
  <LiteSchedule {...componentProps} {...rest} />
59
57
  </Sentry.ErrorBoundary>
60
58
  </div>
@@ -0,0 +1,30 @@
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+
4
+ const imageStyles = {
5
+ maxWidth: "100%",
6
+ height: "auto"
7
+ };
8
+
9
+ const ResponsiveImage = ({ src, alt, maxWidth = "100%", padding = "0" }) => {
10
+ const containerStyles = {
11
+ maxWidth: maxWidth,
12
+ padding: padding,
13
+ display: "flex",
14
+ justifyContent: "center"
15
+ };
16
+ return (
17
+ <div style={containerStyles}>
18
+ <img src={src} alt={alt} style={imageStyles} />
19
+ </div>
20
+ );
21
+ };
22
+
23
+ ResponsiveImage.propTypes = {
24
+ src: PropTypes.string.isRequired,
25
+ alt: PropTypes.string.isRequired,
26
+ maxWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
27
+ padding: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
28
+ };
29
+
30
+ export default ResponsiveImage;
@@ -22,7 +22,7 @@ import { DefaultScrollBehaviour as ScrollBehaviour } from '@utils/scroll';
22
22
  import './ticket-popup-edit-details-form.scss';
23
23
  import { useTicketAssignedContext } from '../../../context/TicketAssignedContext';
24
24
 
25
- const noOpFn = () => {};
25
+ const noop = () => {};
26
26
 
27
27
  export const TicketPopupEditDetailsForm = ({
28
28
  ticket,
@@ -277,7 +277,7 @@ export const TicketPopupEditDetailsForm = ({
277
277
  placeholder={t("ticket_popup.edit_first_name_placeholder")}
278
278
  value={formik.values[TicketKeys.firstName]}
279
279
  onBlur={formik.handleBlur}
280
- onChange={!!initialValues[TicketKeys.firstName] ? noOpFn : formik.handleChange}
280
+ onChange={!!initialValues[TicketKeys.firstName] ? noop : formik.handleChange}
281
281
  disabled={!!initialValues[TicketKeys.firstName]}
282
282
  />
283
283
  {(formik.touched[TicketKeys.firstName] || triedSubmitting) && formik.errors[TicketKeys.firstName] &&
@@ -297,7 +297,7 @@ export const TicketPopupEditDetailsForm = ({
297
297
  placeholder={t("ticket_popup.edit_last_name_placeholder")}
298
298
  value={formik.values[TicketKeys.lastName]}
299
299
  onBlur={formik.handleBlur}
300
- onChange={!!initialValues[TicketKeys.lastName] ? noOpFn : formik.handleChange}
300
+ onChange={!!initialValues[TicketKeys.lastName] ? noop : formik.handleChange}
301
301
  disabled={!!initialValues[TicketKeys.lastName]}
302
302
  />
303
303
  {(formik.touched[TicketKeys.lastName] || triedSubmitting) && formik.errors[TicketKeys.lastName] &&
@@ -317,7 +317,7 @@ export const TicketPopupEditDetailsForm = ({
317
317
  placeholder={t("ticket_popup.edit_company_placeholder")}
318
318
  value={formik.values[TicketKeys.company]}
319
319
  onBlur={formik.handleBlur}
320
- onChange={!!initialValues[TicketKeys.company].name ? noOpFn : formik.handleChange}
320
+ onChange={!!initialValues[TicketKeys.company].name ? noop : formik.handleChange}
321
321
  disabled={!!initialValues[TicketKeys.company].name}
322
322
  tabSelectsValue={false}
323
323
  />
@@ -46,5 +46,10 @@
46
46
  "providerLogo": "logo_facebook.svg",
47
47
  "providerLogoSize": 20
48
48
  }
49
- ]
49
+ ],
50
+ "maintenanceMode" : {
51
+ "enabled": false,
52
+ "title": "Site under maintenance",
53
+ "subtitle": "Please reload page shortly"
54
+ }
50
55
  }
@@ -54,8 +54,12 @@ export const marketingPageQuery = graphql`
54
54
  text
55
55
  }
56
56
  widgets {
57
- text {
58
- content
57
+ content {
58
+ display
59
+ body
60
+ }
61
+ schedule {
62
+ title
59
63
  display
60
64
  }
61
65
  disqus {
@@ -78,10 +82,6 @@ export const marketingPageQuery = graphql`
78
82
  alt
79
83
  }
80
84
  }
81
- schedule {
82
- title
83
- display
84
- }
85
85
  }
86
86
  masonry {
87
87
  display
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import HeroComponent from "../components/HeroComponent";
3
+ import useSiteSettings from "@utils/useSiteSettings";
4
+
5
+ import "../styles/bulma.scss";
6
+
7
+ const MaintenancePage = () => {
8
+ const { maintenanceMode: { title, subtitle } } = useSiteSettings();
9
+
10
+ return (
11
+ <HeroComponent
12
+ title={title}
13
+ subtitle={subtitle}
14
+ />
15
+ );
16
+ };
17
+
18
+ export default MaintenancePage;
@@ -14,10 +14,12 @@ $color_gray_light : #DFDFDF;
14
14
  $color_gray_lighter : #F2F2F2;
15
15
  $color_text_input_hints : #c4c4c4;
16
16
  $color_input_text_color_light: #363636;
17
+ $color_input_text_color_disabled_light: #FFFFFF;
17
18
  $color_icon_light: #FFFFFF;
18
19
  $color_input_background_color_light:#fff;
19
20
  $color_input_border_color_light:#dbdbdb;
20
21
  $color_input_text_color_dark: #FFFFFF;
22
+ $color_input_text_color_disabled_dark: #FFFFFF;
21
23
  $color_input_background_color_dark:rgb(24, 26, 27);
22
24
  $color_input_border_color_dark:rgb(58, 63, 65);
23
25
  $color_horizontal_rule_dark: #7B7B7B;
@@ -43,6 +45,7 @@ $color_alerts:#FF0000;
43
45
  --color_gray_light: #{$color_gray_light};
44
46
  --color_gray_lighter: #{$color_gray_lighter};
45
47
  --color_input_text_color: #{$color_input_text_color_light};
48
+ --color_input_text_color_disabled: #{$color_input_text_color_disabled_light};
46
49
  --color_input_background_color: #{$color_input_background_color_light};
47
50
  --color_input_border_color: #{$color_input_border_color_light};
48
51
  --color_text_input_hints: #{$color_text_input_hints_light};
@@ -63,6 +66,7 @@ html[data-theme="DARK"] {
63
66
  --color_gray_light: #{$color_gray_dark} !important;
64
67
  --color_gray_lighter: #{$color_gray_darker} !important;
65
68
  --color_input_text_color: #{$color_input_text_color_dark} !important;
69
+ --color_input_text_color_disabled: #{$color_input_text_color_disabled_dark} !important;
66
70
  --color_input_background_color: #{$color_input_background_color_dark} !important;
67
71
  --color_input_border_color: #{$color_input_border_color_dark} !important;
68
72
  --color_text_input_hints: #{$color_text_input_hints_dark} !important;
@@ -1,8 +1,10 @@
1
1
 
2
- .left-column {
2
+ .leftColumn {
3
+ overflow-y: auto;
3
4
  }
4
- .right-column {
5
- .image-slider {
5
+
6
+ .rightColumn {
7
+ .imageSlider {
6
8
  height: auto;
7
9
  background-position: center;
8
10
  background-repeat: no-repeat;
@@ -10,4 +12,4 @@
10
12
  min-height: 100%;
11
13
  overflow: hidden;
12
14
  }
13
- }
15
+ }
@@ -531,32 +531,6 @@ div.event-feedback-container {
531
531
  bottom: 0;
532
532
  left: 0;
533
533
  right: 0;
534
-
535
- &-marketing {
536
- position: absolute;
537
- overflow-y: auto;
538
- top: 95px;
539
- right: 0;
540
- left: 20px;
541
- bottom: 0;
542
- padding-right: 20px;
543
- }
544
- }
545
-
546
- .schedule-container {
547
- &-marketing {
548
- display: flex;
549
- flex-direction: column;
550
- margin-top: 15px;
551
- position: absolute;
552
- overflow-y: auto;
553
- top: 95px;
554
- right: 0;
555
- left: 20px;
556
- bottom: 0;
557
- padding-right: 20px;
558
- padding-bottom: 30px;
559
- }
560
534
  }
561
535
  }
562
536
 
@@ -0,0 +1,74 @@
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import { connect } from "react-redux";
4
+ import { graphql } from "gatsby";
5
+ import { Redirect } from "@gatsbyjs/reach-router";
6
+ import { MDXProvider } from "@mdx-js/react";
7
+ import ContentPageTemplate from "./template";
8
+ import Layout from "../../components/Layout";
9
+ import Seo from "../../components/Seo";
10
+ import { titleFromPathname } from "../../utils/urlFormating";
11
+
12
+ import { USER_REQUIREMENTS } from "../../cms/config/collections/contentPagesCollection";
13
+
14
+ const ContentPage = ({
15
+ data,
16
+ isAuthorized,
17
+ isLoggedUser,
18
+ hasTicket,
19
+ children
20
+ }) => {
21
+ const { frontmatter: { title, userRequirement } } = data.mdx;
22
+ if (!isAuthorized && (
23
+ (userRequirement === USER_REQUIREMENTS.loggedIn && !isLoggedUser) ||
24
+ (userRequirement === USER_REQUIREMENTS.hasTicket && !hasTicket)
25
+ )) {
26
+ return <Redirect to="/" noThrow />;
27
+ }
28
+ return (
29
+ <Layout>
30
+ <ContentPageTemplate
31
+ title={title}
32
+ content={children}
33
+ />
34
+ </Layout>
35
+ );
36
+ };
37
+
38
+ ContentPage.propTypes = {
39
+ data: PropTypes.object.isRequired,
40
+ isAuthorized: PropTypes.bool.isRequired,
41
+ isLoggedUser: PropTypes.bool.isRequired,
42
+ hasTicket: PropTypes.bool.isRequired,
43
+ children: PropTypes.node.isRequired
44
+ };
45
+
46
+ const mapStateToProps = ({ loggedUserState, userState }) => ({
47
+ isLoggedUser: loggedUserState.isLoggedUser,
48
+ hasTicket: userState.hasTicket,
49
+ isAuthorized: userState.isAuthorized
50
+ });
51
+
52
+ const StoreConnectedContentPage = connect(mapStateToProps)(ContentPage);
53
+
54
+ export default StoreConnectedContentPage;
55
+
56
+ export const query = graphql`
57
+ query($id: String!) {
58
+ mdx(id: { eq: $id }) {
59
+ frontmatter {
60
+ title
61
+ userRequirement
62
+ }
63
+ }
64
+ }
65
+ `;
66
+
67
+ export const Head = ({
68
+ location
69
+ }) => (
70
+ <Seo
71
+ title={titleFromPathname(location.pathname)}
72
+ location={location}
73
+ />
74
+ );
@@ -0,0 +1,17 @@
1
+ // This file is intended to allow for theme extension with component shadowing.
2
+ // You can add your component imports and map them here to be used as shortcodes in MDX content.
3
+
4
+ // Import components here when needed
5
+ // import Grid from "../../components/Grid";
6
+ // import SpeakerCard from "../../components/SpeakerCard";
7
+ import ResponsiveImage from "../../components/ResponsiveImage";
8
+
9
+ const shortcodes = {
10
+ // Map your components here
11
+ // Example:
12
+ // Grid,
13
+ // SpeakerCard,
14
+ img: ResponsiveImage
15
+ };
16
+
17
+ export default shortcodes;
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+ import PropTypes from "prop-types";
3
+ import { MDXProvider } from "@mdx-js/react";
4
+
5
+ import shortcodes from "./shortcodes";
6
+
7
+ const ContentPageTemplate = ({ title, content }) => (
8
+ <div className="content">
9
+ <h1>{title}</h1>
10
+ <MDXProvider components={shortcodes}>
11
+ {content}
12
+ </MDXProvider>
13
+ </div>
14
+ );
15
+
16
+ export default ContentPageTemplate;
17
+
18
+ ContentPageTemplate.propTypes = {
19
+ title: PropTypes.string.isRequired,
20
+ content: PropTypes.node.isRequired
21
+ };