@blaze-cms/react-page-builder 0.146.0-core-styles.60 → 0.146.0-core-styles.62

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 (36) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/lib/HOC/withInfiniteScroll.js +5 -5
  3. package/lib/HOC/withInfiniteScroll.js.map +1 -1
  4. package/lib/components/Button.js +57 -11
  5. package/lib/components/Button.js.map +1 -1
  6. package/lib/components/List/ListFactory.js.map +1 -1
  7. package/lib/components/MenuItem/MenuItemRender.js +17 -5
  8. package/lib/components/MenuItem/MenuItemRender.js.map +1 -1
  9. package/lib/components/Video/providers/Vimeo/VimeoProvider.js +14 -14
  10. package/lib/components/Video/providers/Vimeo/VimeoProvider.js.map +1 -1
  11. package/lib/hooks/helpers/StoreImages.js +61 -41
  12. package/lib/hooks/helpers/StoreImages.js.map +1 -1
  13. package/lib/hooks/use-page-builder.js +2 -2
  14. package/lib/hooks/use-page-builder.js.map +1 -1
  15. package/lib/variants/LiveBlogList/LiveBlogList.js +45 -48
  16. package/lib/variants/LiveBlogList/LiveBlogList.js.map +1 -1
  17. package/lib-es/components/Button.js +12 -7
  18. package/lib-es/components/Button.js.map +1 -1
  19. package/lib-es/components/List/ListFactory.js +1 -1
  20. package/lib-es/components/List/ListFactory.js.map +1 -1
  21. package/lib-es/components/MenuItem/MenuItemRender.js +16 -3
  22. package/lib-es/components/MenuItem/MenuItemRender.js.map +1 -1
  23. package/lib-es/hooks/helpers/StoreImages.js +44 -30
  24. package/lib-es/hooks/helpers/StoreImages.js.map +1 -1
  25. package/lib-es/variants/LiveBlogList/LiveBlogList.js +41 -51
  26. package/lib-es/variants/LiveBlogList/LiveBlogList.js.map +1 -1
  27. package/package.json +3 -3
  28. package/src/components/Button.js +13 -9
  29. package/src/components/List/ListFactory.js +1 -1
  30. package/src/components/MenuItem/MenuItemRender.js +17 -3
  31. package/src/hooks/helpers/StoreImages.js +33 -31
  32. package/src/variants/LiveBlogList/LiveBlogList.js +51 -47
  33. package/tests/unit/src/components/MenuItem/MenuItemRender.test.js +25 -2
  34. package/tests/unit/src/components/MenuItem/__snapshots__/MenuItem.test.js.snap +1 -0
  35. package/tests/unit/src/variants/LiveBlogList/LiveBlogList.test.js +121 -0
  36. package/tests/unit/src/variants/LiveBlogList/constants.js +10 -0
@@ -7,89 +7,93 @@ import { BsPinAngleFill } from 'react-icons/bs';
7
7
 
8
8
  dayjs.extend(relativeTime);
9
9
 
10
- const LiveBlogList = props => {
11
- const { children, date, published, featured } = props;
10
+ const COLLAPSE_HEIGHT = 380;
11
+
12
+ const LiveBlogList = ({ children, date, published, featured, alternativeHeadline }) => {
12
13
  const [isExpanded, setIsExpanded] = useState(false);
13
14
  const [shouldShowButton, setShouldShowButton] = useState(false);
14
15
  const [{ displayedDate, displayedTime }, setDisplayedDate] = useState({
15
16
  displayedDate: ' ',
16
- displayTime: ''
17
+ displayedTime: ''
17
18
  });
18
- const usedDate = date || published;
19
-
19
+ const usedDate = date;
20
20
  const contentRef = useRef(null);
21
21
 
22
22
  useEffect(
23
23
  () => {
24
- const timeout = setTimeout(() => {
25
- if (contentRef.current) {
26
- const contentHeight = contentRef.current.scrollHeight;
27
- if (contentHeight > 380) {
28
- setShouldShowButton(true);
29
- }
30
- }
31
- }, 500); // delay to wait for content load
32
- // todo: change to use mutation observer
24
+ const el = contentRef.current;
25
+ if (!el) return;
33
26
 
34
- return () => {
35
- clearTimeout(timeout);
36
- };
27
+ const check = () => setShouldShowButton(el.scrollHeight > COLLAPSE_HEIGHT);
28
+
29
+ if (typeof ResizeObserver !== 'undefined') {
30
+ check();
31
+ const ro = new ResizeObserver(check);
32
+ ro.observe(el);
33
+ return () => ro.disconnect();
34
+ }
35
+ check();
37
36
  },
38
37
  [children]
39
38
  );
40
39
 
41
40
  useEffect(
42
41
  () => {
43
- // delay setting time so no SSR issues based on timezone
44
- let newDate = '';
45
- if (usedDate) {
46
- const diffInDays = dayjs().diff(dayjs(usedDate), 'day');
47
- if (diffInDays < 1) {
48
- newDate = dayjs(usedDate).fromNow();
49
- } else if (diffInDays < 365) {
50
- newDate = dayjs(usedDate).format('DD MMM');
51
- } else {
52
- newDate = dayjs(usedDate).format('DD MMM YYYY');
53
- }
54
- setDisplayedDate({
55
- displayedDate: newDate,
56
- displayedTime: dayjs(usedDate).format('HH:mm')
57
- });
58
- }
42
+ if (!usedDate) return;
43
+ const days = dayjs().diff(dayjs(usedDate), 'day');
44
+ const lessThanAYear = days < 365;
45
+
46
+ let newDate;
47
+ if (days < 1) newDate = dayjs(usedDate).fromNow();
48
+ else if (lessThanAYear) newDate = dayjs(usedDate).format('DD MMM');
49
+ else newDate = dayjs(usedDate).format('DD MMM YYYY');
50
+
51
+ setDisplayedDate({
52
+ displayedDate: newDate,
53
+ displayedTime: lessThanAYear ? dayjs(usedDate).format('HH:mm') : null
54
+ });
59
55
  },
60
- [date, published, usedDate]
56
+ [usedDate]
61
57
  );
62
58
 
63
- const toggleExpanded = () => {
64
- setIsExpanded(!isExpanded);
65
- };
66
-
59
+ const toggleExpanded = () => setIsExpanded(value => !value);
67
60
  const isCollapsed = !isExpanded;
68
- const baseClass = 'live-blog-container';
69
- const modifier = featured ? ` ${baseClass}--featured` : '';
70
- const className = `${baseClass}${modifier}`;
61
+ const showHeader = usedDate || alternativeHeadline || featured;
71
62
 
72
63
  return (
73
- <div className={className}>
74
- {usedDate && (
64
+ <div className={`live-blog-container${featured ? ' live-blog-container--featured' : ''}`}>
65
+ {showHeader && (
75
66
  <div className="live-blog-date">
76
67
  {featured && <BsPinAngleFill />}
77
- <span className="live-blog-date--date">{displayedDate}</span>
78
- <span className="live-blog-date--time">{displayedTime}</span>
68
+ {usedDate && (
69
+ <>
70
+ <span className="live-blog-date--date">{displayedDate}</span>
71
+ {displayedTime && <span className="live-blog-date--time">{displayedTime}</span>}
72
+ </>
73
+ )}
74
+ {alternativeHeadline && (
75
+ <span className="live-blog-date--headline">{alternativeHeadline}</span>
76
+ )}
79
77
  </div>
80
78
  )}
81
79
 
82
80
  <div
83
81
  ref={contentRef}
84
82
  className={`live-blog-content ${
85
- isCollapsed ? 'live-blog-content--collapsed' : 'live-blog-content--opened'
83
+ isCollapsed
84
+ ? 'live-blog-content--collapsed collapsed'
85
+ : 'live-blog-content--opened opened'
86
86
  }`}>
87
87
  {children}
88
88
  </div>
89
89
 
90
90
  {shouldShowButton && (
91
91
  <div className="live-blog-button-wrapper">
92
- <Button className="live-blog-toggle" onClick={toggleExpanded} type="button">
92
+ <Button
93
+ aria-label={isExpanded ? 'Show Less' : 'Show More'}
94
+ className="live-blog-toggle"
95
+ onClick={toggleExpanded}
96
+ type="button">
93
97
  {isExpanded ? (
94
98
  <>
95
99
  <FaMinus className="toggle-icon" /> Show Less
@@ -7,6 +7,8 @@ import { render, fireEvent } from '@testing-library/react';
7
7
  import '@testing-library/jest-dom/extend-expect';
8
8
  import MenuRender from '../../../../../src/components/MenuItem/MenuItemRender';
9
9
 
10
+ const MENU_ITEM_CHILDREN_CLASS = 'menu--item-children';
11
+
10
12
  let mockAsPathValue = '/';
11
13
 
12
14
  jest.mock('next/router', () => {
@@ -75,7 +77,7 @@ describe('MenuRender component', () => {
75
77
  );
76
78
 
77
79
  fireEvent.mouseEnter(getByText('Parent'));
78
- let childContainer = container.querySelector('.menu--item-children');
80
+ let childContainer = container.querySelector(`.${MENU_ITEM_CHILDREN_CLASS}`);
79
81
  expect(childContainer).not.toHaveClass('hidden');
80
82
 
81
83
  mockAsPathValue = '/new-route';
@@ -85,7 +87,28 @@ describe('MenuRender component', () => {
85
87
  </MenuRender>
86
88
  );
87
89
 
88
- childContainer = container.querySelector('.menu--item-children');
90
+ childContainer = container.querySelector(`.${MENU_ITEM_CHILDREN_CLASS}`);
91
+ expect(childContainer).toHaveClass('hidden');
92
+ });
93
+
94
+ it('toggles display of children when chevron icon is clicked', () => {
95
+ const { container } = render(
96
+ <MenuRender eventType="click" text="Parent">
97
+ <div data-testid="child">Child</div>
98
+ </MenuRender>
99
+ );
100
+ let childContainer = container.querySelector(`.${MENU_ITEM_CHILDREN_CLASS}`);
101
+ expect(childContainer).toHaveClass('hidden');
102
+
103
+ const chevronIcon = container.querySelector('.menu--item--link--icon');
104
+ expect(chevronIcon).toBeInTheDocument();
105
+
106
+ chevronIcon.click();
107
+ childContainer = container.querySelector(`.${MENU_ITEM_CHILDREN_CLASS}`);
108
+ expect(childContainer).not.toHaveClass('hidden');
109
+
110
+ chevronIcon.click();
111
+ childContainer = container.querySelector(`.${MENU_ITEM_CHILDREN_CLASS}`);
89
112
  expect(childContainer).toHaveClass('hidden');
90
113
  });
91
114
  });
@@ -17,6 +17,7 @@ exports[`MenuItem component should render a MenuItem based on passed parameters
17
17
  <i
18
18
  class="menu--item--link--icon"
19
19
  role="button"
20
+ tabindex="0"
20
21
  >
21
22
  <svg
22
23
  fill="currentColor"
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+ import React from 'react';
5
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
6
+ import '@testing-library/jest-dom/extend-expect';
7
+ import dayjs from 'dayjs';
8
+ import LiveBlogList from '../../../../../src/variants/LiveBlogList/LiveBlogList';
9
+ import { LONG_TEXT, SHORT_TEXT } from './constants';
10
+
11
+ describe('LiveBlogList', () => {
12
+ afterEach(() => {
13
+ jest.restoreAllMocks();
14
+ });
15
+
16
+ it('short content: uses date and hides toggle button', async () => {
17
+ jest.spyOn(HTMLElement.prototype, 'scrollHeight', 'get').mockReturnValue(200);
18
+ const dateProps = { date: '2025-04-15T11:26:03.863Z' };
19
+
20
+ const { container } = render(
21
+ <LiveBlogList {...dateProps}>
22
+ <h2>Short item</h2>
23
+ <p>{SHORT_TEXT}</p>
24
+ </LiveBlogList>
25
+ );
26
+
27
+ const contentElement = container.querySelector('.live-blog-content');
28
+ expect(contentElement).toBeInTheDocument();
29
+ expect(container.querySelector('.live-blog-toggle')).not.toBeInTheDocument();
30
+ const displayedDate = container.querySelector('.live-blog-date');
31
+
32
+ expect(displayedDate).toBeInTheDocument();
33
+ expect(displayedDate.textContent).not.toBe('');
34
+ });
35
+
36
+ it('long content: shows toggle button and lets you expand and collapse', async () => {
37
+ jest.spyOn(HTMLElement.prototype, 'scrollHeight', 'get').mockReturnValue(500);
38
+ const dateProps = { date: '2025-04-15T15:51:59.303Z' };
39
+
40
+ const { container } = render(
41
+ <LiveBlogList {...dateProps}>
42
+ <h3>The day so far</h3>
43
+ <div>{LONG_TEXT}</div>
44
+ </LiveBlogList>
45
+ );
46
+
47
+ const toggleButton = await screen.findByRole('button', { name: /show more/i });
48
+ expect(toggleButton).toBeInTheDocument();
49
+ const contentElement = container.querySelector('.live-blog-content');
50
+ expect(contentElement).toHaveClass('collapsed');
51
+
52
+ fireEvent.click(toggleButton);
53
+ await waitFor(() => {
54
+ expect(contentElement).not.toHaveClass('collapsed');
55
+ expect(toggleButton.textContent).toMatch(/show less/i);
56
+ });
57
+
58
+ fireEvent.click(toggleButton);
59
+ await waitFor(() => {
60
+ expect(contentElement).toHaveClass('collapsed');
61
+ expect(toggleButton.textContent).toMatch(/show more/i);
62
+ });
63
+
64
+ const displayedDate = container.querySelector('.live-blog-date');
65
+ expect(displayedDate).toBeInTheDocument();
66
+ expect(displayedDate.textContent).not.toBe('');
67
+ });
68
+
69
+ it('if date less than 1 day ago, shows relative time', () => {
70
+ const recentDate = '2025-04-15T15:52:00.000Z';
71
+ jest.useFakeTimers('modern');
72
+ jest.setSystemTime(new Date('2025-04-16T10:00:00.000Z'));
73
+
74
+ const { container } = render(
75
+ <LiveBlogList date={recentDate}>
76
+ <p>Less than 1 day old content</p>
77
+ </LiveBlogList>
78
+ );
79
+
80
+ const dateElement = container.querySelector('.live-blog-date');
81
+ expect(dateElement).toBeInTheDocument();
82
+ expect(dateElement.textContent).toMatch(/ago/i);
83
+ jest.useRealTimers();
84
+ });
85
+
86
+ it('if older than a day but within a year, shows date in DD MMM format', () => {
87
+ const fewDaysOld = '2025-04-09T00:00:00.000Z';
88
+ jest.useFakeTimers('modern');
89
+ jest.setSystemTime(new Date('2025-04-16T10:00:00.000Z'));
90
+
91
+ const { container } = render(
92
+ <LiveBlogList date={fewDaysOld}>
93
+ <p>Older than 1 day but less than a year</p>
94
+ </LiveBlogList>
95
+ );
96
+
97
+ const dateElement = container.querySelector('.live-blog-date--date');
98
+ expect(dateElement).toBeInTheDocument();
99
+ const expectedDate = dayjs(fewDaysOld).format('DD MMM');
100
+ expect(dateElement.textContent).toBe(expectedDate);
101
+ jest.useRealTimers();
102
+ });
103
+
104
+ it('if older than a year, shows date in DD MMM YYYY format', () => {
105
+ const oldDate = '2023-03-15T00:00:00.000Z';
106
+ jest.useFakeTimers('modern');
107
+ jest.setSystemTime(new Date('2025-04-16T10:00:00.000Z'));
108
+
109
+ const { container } = render(
110
+ <LiveBlogList date={oldDate}>
111
+ <p>More than 1 year old content</p>
112
+ </LiveBlogList>
113
+ );
114
+
115
+ const dateElement = container.querySelector('.live-blog-date');
116
+ expect(dateElement).toBeInTheDocument();
117
+ const expectedDate = dayjs(oldDate).format('DD MMM YYYY');
118
+ expect(dateElement.textContent).toBe(expectedDate);
119
+ jest.useRealTimers();
120
+ });
121
+ });
@@ -0,0 +1,10 @@
1
+ const LONG_TEXT = `
2
+ In case you’re just joining us, here’s a recap of the latest news and global market moves since the European market open. (You can see the earlier round-up here)
3
+ Europe’s main indexes started on a surprisingly positive note at the start of trading, as markets seemed to recover some ground from the tariff sell-off, even as it became clear that Trump had applied 145% tariffs on Chinese goods
4
+ Mining giants, supported in part by growing appetite for safe haven assets like gold, were leading the FTSE 100 – including Fresnillo, Glencore, and Anglo American
5
+ There was also some optimism emanating from the UK GDP figures, which showed the economy expanding by more than expected in February at 0.5%
6
+ `;
7
+
8
+ const SHORT_TEXT = `Winners of the 2022 World Superyacht Awards have been revealed. From the newly crowned Motor Yacht of the Year to the prestigious Voyagers Award, find out which yachts...`;
9
+
10
+ export { LONG_TEXT, SHORT_TEXT };