@blaze-cms/react-page-builder 0.147.0-rc-eagle.2 → 0.147.0-rc-eagle.6

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 (71) hide show
  1. package/lib/components/ContentGroup/ContentGroup.js +40 -8
  2. package/lib/components/ContentGroup/ContentGroup.js.map +1 -1
  3. package/lib/components/ContentGroup/ContentGroupDataFetcher.js +96 -0
  4. package/lib/components/ContentGroup/ContentGroupDataFetcher.js.map +1 -0
  5. package/lib/components/ContentGroup/helpers/build-data-sections.js +46 -0
  6. package/lib/components/ContentGroup/helpers/build-data-sections.js.map +1 -0
  7. package/lib/components/ContentGroup/helpers/index.js +7 -0
  8. package/lib/components/ContentGroup/helpers/index.js.map +1 -1
  9. package/lib/components/ContentGroupSection/ContentGroupSection.js +3 -2
  10. package/lib/components/ContentGroupSection/ContentGroupSection.js.map +1 -1
  11. package/lib/components/Iframe/Iframe.js +16 -6
  12. package/lib/components/Iframe/Iframe.js.map +1 -1
  13. package/lib/components/Layout/Layout.js +14 -3
  14. package/lib/components/Layout/Layout.js.map +1 -1
  15. package/lib/components/Menu/Menu.js +4 -1
  16. package/lib/components/Menu/Menu.js.map +1 -1
  17. package/lib/components/Menu/MenuContext.js +2 -1
  18. package/lib/components/Menu/MenuContext.js.map +1 -1
  19. package/lib/components/MenuItem/MenuItemRender.js +24 -9
  20. package/lib/components/MenuItem/MenuItemRender.js.map +1 -1
  21. package/lib/components/MenuItem/helpers/has-active-child.js +12 -0
  22. package/lib/components/MenuItem/helpers/has-active-child.js.map +1 -0
  23. package/lib/components/MenuItem/helpers/index.js +14 -0
  24. package/lib/components/MenuItem/helpers/index.js.map +1 -1
  25. package/lib/components/MenuItem/helpers/isUrlPathMatch.js +14 -0
  26. package/lib/components/MenuItem/helpers/isUrlPathMatch.js.map +1 -0
  27. package/lib/helpers/get-component-id.js +9 -3
  28. package/lib/helpers/get-component-id.js.map +1 -1
  29. package/lib-es/components/ContentGroup/ContentGroup.js +41 -9
  30. package/lib-es/components/ContentGroup/ContentGroup.js.map +1 -1
  31. package/lib-es/components/ContentGroup/ContentGroupDataFetcher.js +88 -0
  32. package/lib-es/components/ContentGroup/ContentGroupDataFetcher.js.map +1 -0
  33. package/lib-es/components/ContentGroup/helpers/build-data-sections.js +38 -0
  34. package/lib-es/components/ContentGroup/helpers/build-data-sections.js.map +1 -0
  35. package/lib-es/components/ContentGroup/helpers/index.js +2 -1
  36. package/lib-es/components/ContentGroup/helpers/index.js.map +1 -1
  37. package/lib-es/components/ContentGroupSection/ContentGroupSection.js +3 -2
  38. package/lib-es/components/ContentGroupSection/ContentGroupSection.js.map +1 -1
  39. package/lib-es/components/Iframe/Iframe.js +16 -6
  40. package/lib-es/components/Iframe/Iframe.js.map +1 -1
  41. package/lib-es/components/Layout/Layout.js +14 -3
  42. package/lib-es/components/Layout/Layout.js.map +1 -1
  43. package/lib-es/components/Menu/Menu.js +4 -1
  44. package/lib-es/components/Menu/Menu.js.map +1 -1
  45. package/lib-es/components/Menu/MenuContext.js +2 -1
  46. package/lib-es/components/Menu/MenuContext.js.map +1 -1
  47. package/lib-es/components/MenuItem/MenuItemRender.js +25 -11
  48. package/lib-es/components/MenuItem/MenuItemRender.js.map +1 -1
  49. package/lib-es/components/MenuItem/helpers/has-active-child.js +5 -0
  50. package/lib-es/components/MenuItem/helpers/has-active-child.js.map +1 -0
  51. package/lib-es/components/MenuItem/helpers/index.js +3 -1
  52. package/lib-es/components/MenuItem/helpers/index.js.map +1 -1
  53. package/lib-es/components/MenuItem/helpers/isUrlPathMatch.js +8 -0
  54. package/lib-es/components/MenuItem/helpers/isUrlPathMatch.js.map +1 -0
  55. package/lib-es/helpers/get-component-id.js +9 -3
  56. package/lib-es/helpers/get-component-id.js.map +1 -1
  57. package/package.json +10 -11
  58. package/src/components/ContentGroup/ContentGroup.js +54 -8
  59. package/src/components/ContentGroup/ContentGroupDataFetcher.js +74 -0
  60. package/src/components/ContentGroup/helpers/build-data-sections.js +35 -0
  61. package/src/components/ContentGroup/helpers/index.js +2 -1
  62. package/src/components/ContentGroupSection/ContentGroupSection.js +2 -2
  63. package/src/components/Iframe/Iframe.js +22 -6
  64. package/src/components/Layout/Layout.js +13 -2
  65. package/src/components/Menu/Menu.js +3 -1
  66. package/src/components/Menu/MenuContext.js +1 -1
  67. package/src/components/MenuItem/MenuItemRender.js +40 -12
  68. package/src/components/MenuItem/helpers/has-active-child.js +10 -0
  69. package/src/components/MenuItem/helpers/index.js +3 -1
  70. package/src/components/MenuItem/helpers/isUrlPathMatch.js +10 -0
  71. package/src/helpers/get-component-id.js +7 -4
@@ -3,14 +3,27 @@ import PropTypes from 'prop-types';
3
3
  import { CONTENT_GROUP_TYPES } from './constants';
4
4
  import ContentGroupTabs from './ContentGroupTabs';
5
5
  import ContentGroupAccordion from './ContentGroupAccordion';
6
- import { getSectionsData } from './helpers';
6
+ import ContentGroupDataFetcher from './ContentGroupDataFetcher';
7
+ import { getSectionsData, buildDataSections } from './helpers';
7
8
  import getStructuredDataProperties from './helpers/get-structured-data-properties';
8
9
  import { getCustomHtmlProperties } from '../../helpers';
9
10
 
10
11
  const VALID_TYPES = Object.values(CONTENT_GROUP_TYPES);
11
12
 
12
- const ContentGroup = ({ contentType, isFaqContent, children, customHtmlProps, ...props }) => {
13
- const groupSections = children?.props?.children?.[1];
13
+ const ContentGroup = ({
14
+ contentType,
15
+ isFaqContent,
16
+ children,
17
+ customHtmlProps,
18
+ simpleData,
19
+ dataProperty,
20
+ ...props
21
+ }) => {
22
+ const hasSimpleData = Array.isArray(simpleData) && simpleData.length > 0;
23
+ const dataPropertyName = Array.isArray(dataProperty) ? dataProperty[0] : dataProperty;
24
+ const hasDataProperty = !!dataPropertyName;
25
+
26
+ const childGroupSections = children?.props?.children?.[1];
14
27
 
15
28
  const GroupComponent = useMemo(
16
29
  () =>
@@ -30,9 +43,32 @@ const ContentGroup = ({ contentType, isFaqContent, children, customHtmlProps, ..
30
43
  };
31
44
  }, [isFaqContent, customHtmlProps]);
32
45
 
33
- if (!groupSections || !groupSections.length || !VALID_TYPES.includes(contentType)) return '';
46
+ if (!VALID_TYPES.includes(contentType)) return '';
47
+
48
+ if (!hasSimpleData && hasDataProperty) {
49
+ return (
50
+ <WrapperComponent {...topWrapperProps}>
51
+ <ContentGroupDataFetcher
52
+ dataProperty={dataPropertyName}
53
+ contentType={contentType}
54
+ {...props}
55
+ {...schemaProperties}
56
+ />
57
+ </WrapperComponent>
58
+ );
59
+ }
60
+
61
+ let groupSections;
62
+ let sectionsData;
34
63
 
35
- const sectionsData = getSectionsData(groupSections);
64
+ if (hasSimpleData) {
65
+ ({ groupSections, sectionsData } = buildDataSections(simpleData));
66
+ } else {
67
+ groupSections = childGroupSections;
68
+ if (!groupSections || !groupSections.length) return '';
69
+ sectionsData = getSectionsData(groupSections);
70
+ }
71
+ if (!groupSections || !groupSections.length) return '';
36
72
 
37
73
  return (
38
74
  <WrapperComponent {...topWrapperProps}>
@@ -48,7 +84,7 @@ const ContentGroup = ({ contentType, isFaqContent, children, customHtmlProps, ..
48
84
  };
49
85
 
50
86
  ContentGroup.propTypes = {
51
- children: PropTypes.object.isRequired,
87
+ children: PropTypes.object,
52
88
  contentType: PropTypes.oneOf(VALID_TYPES).isRequired,
53
89
  isFaqContent: PropTypes.bool,
54
90
  customHtmlProps: PropTypes.arrayOf(
@@ -56,12 +92,22 @@ ContentGroup.propTypes = {
56
92
  name: PropTypes.string,
57
93
  value: PropTypes.string
58
94
  })
59
- )
95
+ ),
96
+ simpleData: PropTypes.arrayOf(
97
+ PropTypes.shape({
98
+ label: PropTypes.string,
99
+ text: PropTypes.string
100
+ })
101
+ ),
102
+ dataProperty: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)])
60
103
  };
61
104
 
62
105
  ContentGroup.defaultProps = {
106
+ children: null,
63
107
  isFaqContent: false,
64
- customHtmlProps: []
108
+ customHtmlProps: [],
109
+ simpleData: null,
110
+ dataProperty: null
65
111
  };
66
112
 
67
113
  export default ContentGroup;
@@ -0,0 +1,74 @@
1
+ import React, { useContext, useMemo } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useQuery, gql } from '@apollo/client';
4
+ import { MainContext } from '@blaze-cms/nextjs-components';
5
+ import { getSingleEntitySchema, generateSingleItemQuery } from '../../application/query';
6
+ import ContentGroupTabs from './ContentGroupTabs';
7
+ import ContentGroupAccordion from './ContentGroupAccordion';
8
+ import { buildDataSections } from './helpers';
9
+ import { CONTENT_GROUP_TYPES } from './constants';
10
+
11
+ const PLACEHOLDER_QUERY = gql`
12
+ query {
13
+ __typename
14
+ }
15
+ `;
16
+
17
+ const ContentGroupDataFetcher = ({ dataProperty, parent, contentType, ...props }) => {
18
+ const { isPreview } = useContext(MainContext);
19
+ const dataPropertyName = Array.isArray(dataProperty) ? dataProperty[0] : dataProperty;
20
+
21
+ const { data: schemaData, loading: schemaLoading } = useQuery(getSingleEntitySchema, {
22
+ variables: { id: parent?.itemEntity },
23
+ skip: !parent?.itemEntity || !dataPropertyName
24
+ });
25
+
26
+ const dataQuery = useMemo(() => {
27
+ if (!schemaData) return null;
28
+ const { getEntitySchema } = schemaData;
29
+ const { actions: { getPublished, get } = {} } = getEntitySchema;
30
+ const actionKey = isPreview ? get : getPublished || get;
31
+ if (!actionKey) return null;
32
+ return generateSingleItemQuery(actionKey, `${dataPropertyName} { label text }`);
33
+ }, [schemaData, isPreview, dataPropertyName]);
34
+
35
+ const { data: recordData, loading: recordLoading } = useQuery(dataQuery || PLACEHOLDER_QUERY, {
36
+ variables: { id: parent?.itemId },
37
+ skip: !dataQuery || !parent?.itemId
38
+ });
39
+
40
+ if (schemaLoading || recordLoading) return '';
41
+ if (!recordData?.entityData) return null;
42
+
43
+ const propertyData = recordData.entityData[dataPropertyName];
44
+ if (!propertyData || !propertyData.length) return null;
45
+
46
+ const { groupSections, sectionsData } = buildDataSections(propertyData);
47
+
48
+ if (!groupSections.length) return null;
49
+
50
+ const GroupComponent =
51
+ contentType === CONTENT_GROUP_TYPES.ACCORDION ? ContentGroupAccordion : ContentGroupTabs;
52
+
53
+ return (
54
+ <GroupComponent
55
+ contentType={contentType}
56
+ groupSections={groupSections}
57
+ sectionsData={sectionsData}
58
+ {...props}
59
+ />
60
+ );
61
+ };
62
+
63
+ ContentGroupDataFetcher.propTypes = {
64
+ dataProperty: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
65
+ parent: PropTypes.object,
66
+ contentType: PropTypes.string.isRequired
67
+ };
68
+
69
+ ContentGroupDataFetcher.defaultProps = {
70
+ dataProperty: null,
71
+ parent: null
72
+ };
73
+
74
+ export default ContentGroupDataFetcher;
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import dynamic from 'next/dynamic';
3
+ import getComponentId from '../../../helpers/get-component-id';
4
+
5
+ const TextBlock = dynamic(() => import('../../TextBlock'));
6
+ const ContentGroupSection = dynamic(() => import('../../ContentGroupSection'));
7
+
8
+ const buildDataSections = (items = []) => {
9
+ const itemData = items.map((item, i) => {
10
+ if (!item || !item.text || !item.label) return null;
11
+
12
+ const sectionName = getComponentId(item.label || `section-${i}`, true, true);
13
+ return {
14
+ sectionName,
15
+ label: item.label,
16
+ sectionContent: (
17
+ <ContentGroupSection key={sectionName} name={sectionName} disableChildCheck>
18
+ <TextBlock html={item.text} name={`${sectionName}-text`} />
19
+ </ContentGroupSection>
20
+ )
21
+ };
22
+ });
23
+
24
+ return itemData.filter(Boolean).reduce(
25
+ (acc, { sectionName, label, sectionContent }) => {
26
+ if (!sectionName || !sectionContent) return acc;
27
+ acc.groupSections.push(sectionContent);
28
+ acc.sectionsData.push([sectionName, label]);
29
+ return acc;
30
+ },
31
+ { groupSections: [], sectionsData: [] }
32
+ );
33
+ };
34
+
35
+ export default buildDataSections;
@@ -1,4 +1,5 @@
1
1
  import getSectionsData from './get-sections-data';
2
2
  import getActiveTab from './get-active-tab';
3
+ import buildDataSections from './build-data-sections';
3
4
 
4
- export { getSectionsData, getActiveTab };
5
+ export { getSectionsData, getActiveTab, buildDataSections };
@@ -2,8 +2,8 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { hasChildren } from '../../helpers';
4
4
 
5
- const ContentGroupSection = ({ children, name }) => {
6
- if (!hasChildren(children)) return null;
5
+ const ContentGroupSection = ({ children, name, disableChildCheck }) => {
6
+ if (!disableChildCheck && !hasChildren(children)) return null;
7
7
 
8
8
  return (
9
9
  <div key={name} className="content-group-section">
@@ -1,10 +1,18 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import IframeResizer from 'iframe-resizer-react';
4
+ import { useStringTemplate } from '@blaze-cms/utils-handlebars';
4
5
  import { IFRAME_CLASS, DEFAULT_WIDTH } from './constants';
5
6
 
6
- const Iframe = ({ src, modifier, width, height, enableScrolling }) => {
7
- if (!src) return null;
7
+ const UNSAFE_URL_PATTERN = /^(javascript|data):/i;
8
+
9
+ const isSafeUrl = url => !UNSAFE_URL_PATTERN.test(url.trim());
10
+
11
+ const Iframe = ({ src, modifier, width, height, enableScrolling, parent }) => {
12
+ const {
13
+ data: [resolvedSrc]
14
+ } = useStringTemplate(parent, [src || '']);
15
+ if (!resolvedSrc || !isSafeUrl(resolvedSrc)) return null;
8
16
  const modifierString = modifier ? ` ${modifier}` : '';
9
17
  const iframeClassName = `${IFRAME_CLASS}${modifierString}`;
10
18
  const defaultWidth = !width ? DEFAULT_WIDTH : {};
@@ -13,7 +21,7 @@ const Iframe = ({ src, modifier, width, height, enableScrolling }) => {
13
21
  <IframeResizer
14
22
  scrolling={enableScrolling}
15
23
  autoResize
16
- src={src}
24
+ src={resolvedSrc}
17
25
  sizeHeight={!height}
18
26
  height={height}
19
27
  width={width}
@@ -24,13 +32,21 @@ const Iframe = ({ src, modifier, width, height, enableScrolling }) => {
24
32
  };
25
33
 
26
34
  Iframe.propTypes = {
27
- src: PropTypes.string.isRequired,
35
+ src: PropTypes.string,
28
36
  enableScrolling: PropTypes.bool,
29
37
  modifier: PropTypes.string,
30
38
  width: PropTypes.string,
31
- height: PropTypes.string
39
+ height: PropTypes.string,
40
+ parent: PropTypes.object
32
41
  };
33
42
 
34
- Iframe.defaultProps = { enableScrolling: false, modifier: '', width: '', height: '' };
43
+ Iframe.defaultProps = {
44
+ src: '',
45
+ enableScrolling: false,
46
+ modifier: '',
47
+ width: '',
48
+ height: '',
49
+ parent: {}
50
+ };
35
51
 
36
52
  export default Iframe;
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import { useStringTemplate } from '@blaze-cms/utils-handlebars';
3
4
  import Wrapper from '../Wrapper';
4
5
  import { getClassModifiers } from '../../utils';
5
6
  import { useGetImages } from '../../hooks';
@@ -15,6 +16,8 @@ const Layout = React.forwardRef(
15
16
  settings,
16
17
  modifier,
17
18
  backgroundImage,
19
+ backgroundImageUrl,
20
+ parent,
18
21
  tagType,
19
22
  dataNoSnippet,
20
23
  sticky,
@@ -24,10 +27,14 @@ const Layout = React.forwardRef(
24
27
  },
25
28
  ref
26
29
  ) => {
30
+ const {
31
+ data: [resolvedBackgroundImageUrl]
32
+ } = useStringTemplate(parent, [backgroundImageUrl || '']);
27
33
  const {
28
34
  data: { getFile: { url = null } = {} }
29
- } = useGetImages(backgroundImage);
30
- const style = getStylesToUpdate({ backgroundImage: url });
35
+ } = useGetImages(backgroundImage, false, !!resolvedBackgroundImageUrl);
36
+ const backgroundUrl = resolvedBackgroundImageUrl || url;
37
+ const style = getStylesToUpdate({ backgroundImage: backgroundUrl });
31
38
  const { title } = settings;
32
39
  const classModifiers = getClassModifiers(type, { modifier, sticky, ...otherProps });
33
40
  const additionalRowModifier = checkIfRowHasColumns(type, children) ? ' display-row' : '';
@@ -65,6 +72,8 @@ Layout.propTypes = {
65
72
  }),
66
73
  modifier: PropTypes.string,
67
74
  backgroundImage: PropTypes.string,
75
+ backgroundImageUrl: PropTypes.string,
76
+ parent: PropTypes.object,
68
77
  tagType: PropTypes.string,
69
78
  dataNoSnippet: PropTypes.bool,
70
79
  VariantComponent: PropTypes.func,
@@ -82,6 +91,8 @@ Layout.defaultProps = {
82
91
  title: ''
83
92
  },
84
93
  backgroundImage: '',
94
+ backgroundImageUrl: '',
95
+ parent: {},
85
96
  modifier: '',
86
97
  tagType: '',
87
98
  dataNoSnippet: false,
@@ -11,6 +11,7 @@ import BlazeLink from '../BlazeLink';
11
11
  const Menu = ({
12
12
  children,
13
13
  collapse,
14
+ openActiveSubmenus,
14
15
  modifier,
15
16
  mobileMenuModifier,
16
17
  mobileMenuChildrenModifier,
@@ -51,7 +52,7 @@ const Menu = ({
51
52
  });
52
53
 
53
54
  return (
54
- <MenuContext.Provider value={{ showMobileMenu }}>
55
+ <MenuContext.Provider value={{ showMobileMenu, openActiveSubmenus }}>
55
56
  <div className={menuWrapperClasses}>
56
57
  {collapse && (
57
58
  <div className="menu--mobile-wrapper">
@@ -99,6 +100,7 @@ const Menu = ({
99
100
 
100
101
  Menu.propTypes = {
101
102
  collapse: PropTypes.bool.isRequired,
103
+ openActiveSubmenus: PropTypes.bool.isRequired,
102
104
  logoOnMobile: PropTypes.bool.isRequired,
103
105
  logoOnDesktop: PropTypes.bool,
104
106
  logoOnMobileUrl: PropTypes.string,
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
2
 
3
- const MenuContext = React.createContext({ showMobileMenu: false });
3
+ const MenuContext = React.createContext({ showMobileMenu: false, openActiveSubmenus: false });
4
4
 
5
5
  export default MenuContext;
@@ -1,17 +1,23 @@
1
+ /* eslint-disable jsx-a11y/no-static-element-interactions */
1
2
  import React, { useState, useContext, useEffect } from 'react';
2
3
  import PropTypes from 'prop-types';
3
4
  import { FaChevronDown, FaChevronUp } from 'react-icons/fa';
5
+ import classnames from 'classnames';
4
6
  import { useRouter } from 'next/router';
5
7
  import { useStringTemplate } from '@blaze-cms/utils-handlebars';
6
- import { HOVER, MOUSE_ENTER, MOUSE_LEAVE, HIDDEN } from '../../constants';
8
+ import { HOVER, MOUSE_ENTER, MOUSE_LEAVE, HIDDEN, CLICK } from '../../constants';
7
9
  import { hasChildren } from '../../helpers';
8
10
  import BlazeLink from '../BlazeLink';
9
11
  import MenuContext from '../Menu/MenuContext';
10
- import { injectHelperIntoTemplate } from './helpers';
12
+ import { injectHelperIntoTemplate, isUrlPathMatch, hasActiveChild } from './helpers';
11
13
 
12
14
  const MenuItemRender = ({ children, eventType, text, modifier, url, parent }) => {
13
- const [displayChildren, setDisplayChildren] = useState(false);
14
- const { showMobileMenu } = useContext(MenuContext);
15
+ const router = useRouter();
16
+ const { showMobileMenu, openActiveSubmenus } = useContext(MenuContext);
17
+
18
+ const isHoverEvent = eventType === HOVER;
19
+ const isClickEvent = eventType === CLICK;
20
+
15
21
  const {
16
22
  loading: loadingText,
17
23
  data: [textToUse]
@@ -21,14 +27,24 @@ const MenuItemRender = ({ children, eventType, text, modifier, url, parent }) =>
21
27
  data: [urlToUse]
22
28
  } = useStringTemplate(parent, [injectHelperIntoTemplate(url, 'url_encode')]);
23
29
 
24
- const router = useRouter();
25
- const isHoverEvent = eventType === HOVER;
26
- const childrenDisplayClass = displayChildren ? '' : HIDDEN;
30
+ const isActive = router ? isUrlPathMatch(router.asPath, urlToUse) : false;
31
+ const isActiveParent = router ? hasActiveChild(router.asPath, children) : false;
32
+ const shouldPreOpen = openActiveSubmenus && isActiveParent && isClickEvent;
27
33
  const hasValidChildren = hasChildren(children);
28
34
 
35
+ const [displayChildren, setDisplayChildren] = useState(shouldPreOpen);
36
+
29
37
  useEffect(() => {
30
- if (!showMobileMenu) setDisplayChildren(false);
31
- }, [showMobileMenu]);
38
+ if (!showMobileMenu && !shouldPreOpen) setDisplayChildren(false);
39
+ }, [
40
+ children,
41
+ isClickEvent,
42
+ loadingUrl,
43
+ openActiveSubmenus,
44
+ router,
45
+ shouldPreOpen,
46
+ showMobileMenu
47
+ ]);
32
48
 
33
49
  useEffect(() => {
34
50
  if (isHoverEvent) {
@@ -38,6 +54,8 @@ const MenuItemRender = ({ children, eventType, text, modifier, url, parent }) =>
38
54
 
39
55
  if (loadingUrl || loadingText) return '';
40
56
 
57
+ const childrenDisplayClass = displayChildren ? '' : HIDDEN;
58
+
41
59
  const handleItemEvent = ({ type }) => {
42
60
  if (isHoverEvent) {
43
61
  if (type === MOUSE_ENTER) {
@@ -55,15 +73,25 @@ const MenuItemRender = ({ children, eventType, text, modifier, url, parent }) =>
55
73
  }
56
74
  };
57
75
 
76
+ const menuItemLinkClassname = classnames('menu--item--link', {
77
+ 'menu--item--link--active': isActive,
78
+ 'menu--item--link--active-parent': isActiveParent
79
+ });
80
+
58
81
  return (
59
82
  <li className={modifier} onMouseEnter={handleItemEvent} onMouseLeave={handleItemEvent}>
60
83
  <div
61
- className="menu--item--link"
84
+ className={menuItemLinkClassname}
62
85
  onClick={handleMobileClick}
63
86
  role={!urlToUse && hasValidChildren ? 'button' : undefined}
64
87
  tabIndex={!urlToUse && hasValidChildren ? 0 : undefined}>
65
- {urlToUse ? <BlazeLink href={urlToUse}>{textToUse}</BlazeLink> : <span>{textToUse}</span>}
66
-
88
+ {urlToUse ? (
89
+ <BlazeLink href={urlToUse}>{textToUse}</BlazeLink>
90
+ ) : (
91
+ <span role="button" onClick={handleItemEvent}>
92
+ {textToUse}
93
+ </span>
94
+ )}
67
95
  {hasValidChildren && (
68
96
  <i
69
97
  role="button"
@@ -0,0 +1,10 @@
1
+ import isUrlPathMatch from './isUrlPathMatch';
2
+ import { hasChildren } from '../../../helpers';
3
+
4
+ const hasActiveChild = (path, children) =>
5
+ hasChildren(children) &&
6
+ children.props.children[1][0].props.component.items.find(menuItem =>
7
+ isUrlPathMatch(path, menuItem.settings.url)
8
+ );
9
+
10
+ export default hasActiveChild;
@@ -1,3 +1,5 @@
1
1
  import injectHelperIntoTemplate from './inject-helper-into-template';
2
+ import isUrlPathMatch from './isUrlPathMatch';
3
+ import hasActiveChild from './has-active-child';
2
4
 
3
- export { injectHelperIntoTemplate };
5
+ export { injectHelperIntoTemplate, isUrlPathMatch, hasActiveChild };
@@ -0,0 +1,10 @@
1
+ const isUrlPathMatch = (path, itemUrl) => {
2
+ if (!itemUrl || !path) return false;
3
+
4
+ const reg = new RegExp(/^[^#?]+/);
5
+ const _path = reg.exec(path)[0];
6
+
7
+ return _path === itemUrl || _path === `/${itemUrl}`;
8
+ };
9
+
10
+ export default isUrlPathMatch;
@@ -1,9 +1,12 @@
1
1
  import cssesc from 'cssesc';
2
2
  import { COMPONENT_ID_PREFIX } from '../constants';
3
3
 
4
- const getComponentId = componentName =>
5
- cssesc(`${COMPONENT_ID_PREFIX}${componentName.replace(/\s+/g, '-')}`, {
6
- isIdentifier: true
7
- });
4
+ const getComponentId = (componentName, noPrefix = false, toLowerCase = false) => {
5
+ let id = `${noPrefix ? '' : COMPONENT_ID_PREFIX}${componentName.replace(/\s+/g, '-')}`;
6
+ if (toLowerCase) {
7
+ id = id.toLowerCase();
8
+ }
9
+ return cssesc(id, { isIdentifier: true });
10
+ };
8
11
 
9
12
  export default getComponentId;