@eeacms/volto-tabs-block 4.0.1 → 5.0.1

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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,46 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ ### [5.0.1](https://github.com/eea/volto-tabs-block/compare/5.0.0...5.0.1) - 15 May 2023
8
+
9
+ #### :bug: Bug Fixes
10
+
11
+ - fix: show properties for block inside tabs block [Mimi - [`5b4dd3d`](https://github.com/eea/volto-tabs-block/commit/5b4dd3d5f3a045126ead6c33cc8e2e470d691c6d)]
12
+
13
+ ## [5.0.0](https://github.com/eea/volto-tabs-block/compare/4.0.1...5.0.0) - 24 April 2023
14
+
15
+ #### :rocket: New Features
16
+
17
+ - feat(SEO): Render content of all tabs initially for SEO optimization and use CSS to hide non-active ones - refs #250207 [dobri1408 - [`f3499f2`](https://github.com/eea/volto-tabs-block/commit/f3499f2f956bf144ae274f7e4277ae3842e3d206)]
18
+
19
+ #### :bug: Bug Fixes
20
+
21
+ - fix(tabs): scrollToElement on mount of View templates [nileshgulia1 - [`be6a6fb`](https://github.com/eea/volto-tabs-block/commit/be6a6fbba8fde9d5859f1a2408442c9814a72399)]
22
+
23
+ #### :nail_care: Enhancements
24
+
25
+ - refactor(code): Cleanup code as per sonarqube issues [Teodor Voicu - [`5b73ca6`](https://github.com/eea/volto-tabs-block/commit/5b73ca6b504a1b295f992f9536bda6d7a3448d29)]
26
+ - refactor(helpers): make getParentTabFromHash bulletproof refs#153601 [nileshgulia1 - [`890ac8c`](https://github.com/eea/volto-tabs-block/commit/890ac8c0ed4ad2bc38d46283bc945a5b58febbbf)]
27
+
28
+ #### :house: Internal changes
29
+
30
+ - chore(package.json): update package version from 4.0.1 to 5.0.0 [Miu Razvan - [`e0fee6f`](https://github.com/eea/volto-tabs-block/commit/e0fee6f42ed5111db62af156b61fdc5a519e8dc7)]
31
+ - chore: fix eslint refs #153601 [nileshgulia1 - [`79589b7`](https://github.com/eea/volto-tabs-block/commit/79589b792c9a7feabf4a942617398d1ae83277ae)]
32
+
33
+ #### :hammer_and_wrench: Others
34
+
35
+ - Merge pull request #35 from eea/fix-tab-anchors-scroll [Nilesh - [`532f05b`](https://github.com/eea/volto-tabs-block/commit/532f05b76dbbc0e6db7bafd270aad83e9e35a5d5)]
36
+ - clean up code [Miu Razvan - [`0228e2d`](https://github.com/eea/volto-tabs-block/commit/0228e2ddf700fe55db902b9c8b2cf0556b316ec5)]
37
+ - prettier fix [Miu Razvan - [`cb7564c`](https://github.com/eea/volto-tabs-block/commit/cb7564c4379adccf3a0a3880bcdb91fe54db8908)]
38
+ - fix onSelectBlock callback [Miu Razvan - [`5af17e6`](https://github.com/eea/volto-tabs-block/commit/5af17e6c734a5717b99685077a0825aa68e6867f)]
39
+ - revert breaking changes [Miu Razvan - [`ff1fc59`](https://github.com/eea/volto-tabs-block/commit/ff1fc596aee480c79fd42d0bb97570041f215ac8)]
40
+ - bump major version [nileshgulia1 - [`4fbae52`](https://github.com/eea/volto-tabs-block/commit/4fbae52ec06b3830c29b41385095a5b383b01e4b)]
41
+ - move slug check to volto-anchors [nileshgulia1 - [`1e33c22`](https://github.com/eea/volto-tabs-block/commit/1e33c229fac8e9d7ab65941fc7124a213d59e603)]
42
+ - fix scrolling onTabChange [nileshgulia1 - [`7051676`](https://github.com/eea/volto-tabs-block/commit/7051676673d395f142790ac92438c20f3d0b2006)]
43
+ - fix scroll to target [nileshgulia1 - [`02edb7b`](https://github.com/eea/volto-tabs-block/commit/02edb7b8699e9a39f8f8d302fd13e68910d685c4)]
44
+ - scrollToTarget method [nileshgulia1 - [`d6732e1`](https://github.com/eea/volto-tabs-block/commit/d6732e1d2d682010623d1efe38502642b8faa7de)]
45
+ - re add hocs, not a breaking change [nileshgulia1 - [`a1bb60b`](https://github.com/eea/volto-tabs-block/commit/a1bb60b2231d8dd335dc9cb8c3ec9b4f2580639b)]
46
+ - scroll to anchors computed from hash [nileshgulia1 - [`9a3da29`](https://github.com/eea/volto-tabs-block/commit/9a3da294ce0a35d1be2f06d2c4df39e51d49d895)]
7
47
  ### [4.0.1](https://github.com/eea/volto-tabs-block/compare/4.0.0...4.0.1) - 4 April 2023
8
48
 
9
49
  #### :rocket: New Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-tabs-block",
3
- "version": "4.0.1",
3
+ "version": "5.0.1",
4
4
  "description": "volto-tabs-block: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -17,10 +17,12 @@
17
17
  "url": "git@github.com:eea/volto-tabs-block.git"
18
18
  },
19
19
  "addons": [
20
- "@eeacms/volto-block-style"
20
+ "@eeacms/volto-block-style",
21
+ "@eeacms/volto-anchors"
21
22
  ],
22
23
  "dependencies": {
23
24
  "@eeacms/volto-block-style": "*",
25
+ "@eeacms/volto-anchors": "*",
24
26
  "react-responsive-tabs": "4.4.3",
25
27
  "react-slick": "^0.24.0",
26
28
  "slick-carousel": "^1.8.1"
@@ -111,7 +111,7 @@ const Edit = (props) => {
111
111
  }
112
112
  };
113
113
 
114
- const onSelectBlock = (id, tabId, isMultipleSelection, event) => {
114
+ const onSelectBlock = (id, isMultipleSelection, event) => {
115
115
  let newMultiSelected = [];
116
116
  let selected = id;
117
117
 
@@ -220,23 +220,19 @@ const Edit = (props) => {
220
220
  ) : (
221
221
  ''
222
222
  )}
223
- {!data?.readOnlySettings ? (
223
+ {!data?.readOnlySettings && !activeBlock ? (
224
224
  <SidebarPortal selected={props.selected}>
225
- {activeBlock ? (
226
- ''
227
- ) : (
228
- <InlineForm
229
- schema={schemaObject}
230
- title={schemaObject.title}
231
- onChangeField={(id, value) => {
232
- onChangeBlock(block, {
233
- ...data,
234
- [id]: value,
235
- });
236
- }}
237
- formData={data}
238
- />
239
- )}
225
+ <InlineForm
226
+ schema={schemaObject}
227
+ title={schemaObject.title}
228
+ onChangeField={(id, value) => {
229
+ onChangeBlock(block, {
230
+ ...data,
231
+ [id]: value,
232
+ });
233
+ }}
234
+ formData={data}
235
+ />
240
236
  </SidebarPortal>
241
237
  ) : (
242
238
  ''
@@ -51,10 +51,6 @@ class EditBlockWrapper extends React.Component {
51
51
  return;
52
52
  }
53
53
 
54
- // if (this.props.handleClickOutside) {
55
- // this.props.handleClickOutside();
56
- // }
57
-
58
54
  if (this.state.addNewBlockOpened) {
59
55
  this.setState({
60
56
  addNewBlockOpened: false,
@@ -1,8 +1,12 @@
1
1
  import React from 'react';
2
2
  import cx from 'classnames';
3
+ import { compose } from 'redux';
4
+ import { withRouter } from 'react-router';
3
5
  import { StyleWrapperView } from '@eeacms/volto-block-style/StyleWrapper';
4
6
  import { TABS_BLOCK } from '@eeacms/volto-tabs-block/constants';
7
+ import { getParentTabFromHash } from '@eeacms/volto-tabs-block/helpers';
5
8
  import { DefaultView } from './templates/default';
9
+ import { withScrollToTarget } from '@eeacms/volto-tabs-block/hocs';
6
10
 
7
11
  import config from '@plone/volto/registry';
8
12
 
@@ -27,6 +31,30 @@ const View = (props) => {
27
31
  config.blocks.blocksConfig[TABS_BLOCK].templates?.[template]?.view ||
28
32
  DefaultView;
29
33
 
34
+ React.useEffect(() => {
35
+ const urlHash = props.location.hash.substring(1) || '';
36
+ const parentTabId = getParentTabFromHash(data, urlHash);
37
+ const id = parentTabId;
38
+ const index = tabsList.indexOf(id);
39
+ const parentId = data.id || props.id;
40
+ const parent = document.getElementById(parentId);
41
+ const headerWrapper = document.querySelector('.header-wrapper');
42
+ const offsetHeight = headerWrapper?.offsetHeight || 0;
43
+ if (id !== parentId && index > -1 && parent) {
44
+ if (activeTabIndex !== index) {
45
+ setActiveTab(id);
46
+ }
47
+ setTimeout(() => {
48
+ const scrollToElement = document.getElementById(urlHash);
49
+ //TODO: volto now uses react-router-hash-link which automatically scrolls to offset 0
50
+ props.scrollToTarget(scrollToElement, offsetHeight);
51
+ }, 10);
52
+ } else if (id === parentId && parent) {
53
+ props.scrollToTarget(parent, offsetHeight);
54
+ }
55
+ /* eslint-disable-next-line */
56
+ }, []);
57
+
30
58
  return (
31
59
  <StyleWrapperView
32
60
  {...props}
@@ -67,4 +95,4 @@ const View = (props) => {
67
95
  );
68
96
  };
69
97
 
70
- export default View;
98
+ export default compose(withScrollToTarget)(withRouter(View));
@@ -9,6 +9,8 @@ import config from '@plone/volto/registry';
9
9
  import { Icon as VoltoIcon, RenderBlocks } from '@plone/volto/components';
10
10
  import { TABS_BLOCK } from '@eeacms/volto-tabs-block/constants';
11
11
  import { withScrollToTarget } from '@eeacms/volto-tabs-block/hocs';
12
+ import { getParentTabFromHash } from '@eeacms/volto-tabs-block/helpers';
13
+ import noop from 'lodash/noop';
12
14
 
13
15
  import 'react-responsive-tabs/styles.css';
14
16
  import '@eeacms/volto-tabs-block/less/menu.less';
@@ -49,7 +51,7 @@ const View = (props) => {
49
51
  tabsList = [],
50
52
  tabs = {},
51
53
  activeTabIndex = 0,
52
- setActiveTab = () => {},
54
+ setActiveTab = noop,
53
55
  id,
54
56
  } = props;
55
57
 
@@ -59,6 +61,7 @@ const View = (props) => {
59
61
 
60
62
  const tabsContainer = React.useRef();
61
63
  const [mounted, setMounted] = React.useState(false);
64
+ const [hashTab, setHashTab] = React.useState(false);
62
65
  const [initialWidth, setInitialWidth] = React.useState(transformWidth);
63
66
 
64
67
  const schema = React.useMemo(
@@ -101,7 +104,7 @@ const View = (props) => {
101
104
  {title || defaultTitle}{' '}
102
105
  </>
103
106
  ),
104
- getContent: () => (
107
+ content: (
105
108
  <Tab {...props} tab={tab} content={tabs[tab]} aria-hidden={false} />
106
109
  ),
107
110
  key: tab,
@@ -155,11 +158,19 @@ const View = (props) => {
155
158
  ref={tabsContainer}
156
159
  transformWidth={initialWidth}
157
160
  selectedTabKey={tabsList[activeTabIndex]}
161
+ unmountOnExit={false}
158
162
  items={items}
159
163
  onChange={(tab) => {
160
- if (tab !== tabsList[activeTabIndex]) {
164
+ const { blockWidth } = tabsContainer.current?.state || {};
165
+ const tabWithHash = getParentTabFromHash(
166
+ data,
167
+ props.location.hash.substring(1),
168
+ );
169
+ if (tabWithHash === tabsList[activeTabIndex] && !hashTab)
170
+ setHashTab(true);
171
+ else if (tab !== tabsList[activeTabIndex]) {
161
172
  setActiveTab(tab);
162
- } else {
173
+ } else if (blockWidth <= initialWidth) {
163
174
  setActiveTab(null);
164
175
  }
165
176
  }}
@@ -1,11 +1,8 @@
1
1
  import React from 'react';
2
- import { connect } from 'react-redux';
3
- import { compose } from 'redux';
4
2
  import { withRouter } from 'react-router';
5
3
  import loadable from '@loadable/component';
6
4
  import cx from 'classnames';
7
5
  import { Icon, RenderBlocks } from '@plone/volto/components';
8
- import { withScrollToTarget } from '@eeacms/volto-tabs-block/hocs';
9
6
 
10
7
  import rightArrowSVG from '@eeacms/volto-tabs-block/icons/right-arrow.svg';
11
8
  import leftArrowSVG from '@eeacms/volto-tabs-block/icons/left-arrow.svg';
@@ -14,6 +11,8 @@ import 'slick-carousel/slick/slick.css';
14
11
  import 'slick-carousel/slick/slick-theme.css';
15
12
  import '@eeacms/volto-tabs-block/less/carousel.less';
16
13
 
14
+ import noop from 'lodash/noop';
15
+
17
16
  const Slider = loadable(() => import('react-slick'));
18
17
 
19
18
  const Dots = (props) => {
@@ -95,18 +94,15 @@ const ArrowsGroup = (props) => {
95
94
 
96
95
  const View = (props) => {
97
96
  const slider = React.useRef(null);
98
- const [hashlinkOnMount, setHashlinkOnMount] = React.useState(false);
99
97
  const blockId = props.id;
100
98
  const {
101
99
  activeTab = null,
102
100
  data = {},
103
- hashlink = {},
104
101
  metadata = {},
105
102
  tabsList = [],
106
103
  tabs = {},
107
- setActiveTab = () => {},
104
+ setActiveTab = noop,
108
105
  } = props;
109
- const activeTabIndex = tabsList.indexOf(activeTab);
110
106
  const uiContainer = data.align === 'full' ? 'ui container' : false;
111
107
 
112
108
  const settings = {
@@ -151,40 +147,6 @@ const View = (props) => {
151
147
  carouselDiv.setAttribute('tabindex', '0');
152
148
  }, []);
153
149
 
154
- React.useEffect(() => {
155
- const urlHash = props.location.hash.substring(1) || '';
156
- if (
157
- hashlink.counter > 0 ||
158
- (hashlink.counter === 0 && urlHash && !hashlinkOnMount)
159
- ) {
160
- const id = hashlink.hash || urlHash || '';
161
- const index = tabsList.indexOf(id);
162
- const parentId = data.id || props.id;
163
- const parent = document.getElementById(parentId);
164
- // TODO: Find the best way to add offset relative to header
165
- // The header can be static on mobile and relative on > mobile
166
- const headerWrapper = document.querySelector('.header-wrapper');
167
- const offsetHeight = headerWrapper?.offsetHeight || 0;
168
- if (
169
- id !== parentId &&
170
- parentId === hashlink.data.parentId &&
171
- index > -1 &&
172
- parent
173
- ) {
174
- if (activeTabIndex !== index) {
175
- slider.current.slickGoTo(index);
176
- }
177
- props.scrollToTarget(parent, offsetHeight);
178
- } else if (id === parentId && parent) {
179
- props.scrollToTarget(parent, offsetHeight);
180
- }
181
- }
182
- if (!hashlinkOnMount) {
183
- setHashlinkOnMount(true);
184
- }
185
- /* eslint-disable-next-line */
186
- }, [hashlink.counter]);
187
-
188
150
  const panes = tabsList.map((tab, index) => {
189
151
  return {
190
152
  id: tab,
@@ -227,11 +189,4 @@ const View = (props) => {
227
189
  );
228
190
  };
229
191
 
230
- export default compose(
231
- connect((state) => {
232
- return {
233
- hashlink: state.hashlink,
234
- };
235
- }),
236
- withScrollToTarget,
237
- )(withRouter(View));
192
+ export default withRouter(View);
@@ -1,12 +1,8 @@
1
1
  import React from 'react';
2
- import { connect } from 'react-redux';
3
- import { compose } from 'redux';
4
2
  import { withRouter } from 'react-router';
5
3
  import loadable from '@loadable/component';
6
4
  import cx from 'classnames';
7
5
  import { RenderBlocks } from '@plone/volto/components';
8
- import { withScrollToTarget } from '@eeacms/volto-tabs-block/hocs';
9
-
10
6
  import 'slick-carousel/slick/slick.css';
11
7
  import 'slick-carousel/slick/slick-theme.css';
12
8
  import '@eeacms/volto-tabs-block/less/carousel.less';
@@ -15,14 +11,7 @@ const Slider = loadable(() => import('react-slick'));
15
11
 
16
12
  const View = (props) => {
17
13
  const slider = React.useRef(null);
18
- const [hashlinkOnMount, setHashlinkOnMount] = React.useState(false);
19
- const {
20
- metadata = {},
21
- data = {},
22
- tabsList = [],
23
- tabs = {},
24
- hashlink = {},
25
- } = props;
14
+ const { metadata = {}, data = {}, tabsList = [], tabs = {} } = props;
26
15
  const theme = data.theme || 'light';
27
16
  const uiContainer = data.align === 'full' ? 'ui container' : false;
28
17
 
@@ -38,34 +27,6 @@ const View = (props) => {
38
27
  verticalSwiping: true,
39
28
  };
40
29
 
41
- React.useEffect(() => {
42
- const urlHash = props.location.hash.substring(1) || '';
43
- if (
44
- hashlink.counter > 0 ||
45
- (hashlink.counter === 0 && urlHash && !hashlinkOnMount)
46
- ) {
47
- const id = hashlink.hash || urlHash || '';
48
- const index = tabsList.indexOf(id);
49
- const currentIndex = slider.current?.innerSlider?.state?.currentSlide;
50
- const parentId = data.id || props.id;
51
- const parent = document.getElementById(parentId);
52
- const headerWrapper = document.querySelector('.header-wrapper');
53
- const offsetHeight = headerWrapper?.offsetHeight || 0;
54
- if (id !== parentId && index > -1 && parent) {
55
- if (currentIndex !== index) {
56
- slider.current.slickGoTo(index);
57
- }
58
- props.scrollToTarget(parent, offsetHeight);
59
- } else if (id === parentId && parent) {
60
- props.scrollToTarget(parent, offsetHeight);
61
- }
62
- }
63
- if (!hashlinkOnMount) {
64
- setHashlinkOnMount(true);
65
- }
66
- /* eslint-disable-next-line */
67
- }, [hashlink.counter]);
68
-
69
30
  const panes = tabsList.map((tab, index) => {
70
31
  return {
71
32
  id: tab,
@@ -89,11 +50,4 @@ const View = (props) => {
89
50
  );
90
51
  };
91
52
 
92
- export default compose(
93
- connect((state) => {
94
- return {
95
- hashlink: state.hashlink,
96
- };
97
- }),
98
- withScrollToTarget,
99
- )(withRouter(View));
53
+ export default withRouter(View);
@@ -15,6 +15,8 @@ import {
15
15
 
16
16
  import '@eeacms/volto-tabs-block/less/menu.less';
17
17
 
18
+ import noop from 'lodash/noop';
19
+
18
20
  const MenuItem = (props) => {
19
21
  const inputRef = React.useRef(null);
20
22
  const {
@@ -30,11 +32,11 @@ const MenuItem = (props) => {
30
32
  tabsDescription,
31
33
  tabsList = [],
32
34
  tabsTitle,
33
- emptyTab = () => {},
34
- setActiveBlock = () => {},
35
- setActiveTab = () => {},
36
- setEditingTab = () => {},
37
- onChangeBlock = () => {},
35
+ emptyTab = noop,
36
+ setActiveBlock = noop,
37
+ setActiveTab = noop,
38
+ setEditingTab = noop,
39
+ onChangeBlock = noop,
38
40
  } = props;
39
41
  const { tab, index } = props;
40
42
  const title = tabs[tab].title;
@@ -157,11 +159,11 @@ const Edit = (props) => {
157
159
  tabs = {},
158
160
  tabsData = {},
159
161
  tabsList = [],
160
- emptyTab = () => {},
161
- onChangeBlock = () => {},
162
- onChangeTabData = () => {},
163
- onSelectBlock = () => {},
164
- setEditingTab = () => {},
162
+ emptyTab = noop,
163
+ onChangeBlock = noop,
164
+ onChangeTabData = noop,
165
+ onSelectBlock = noop,
166
+ setEditingTab = noop,
165
167
  } = props;
166
168
  const menuPosition = getMenuPosition(data);
167
169
  const isContainer = data.align === 'full';
@@ -242,7 +244,6 @@ const Edit = (props) => {
242
244
  : false;
243
245
  onSelectBlock(
244
246
  id,
245
- activeTab,
246
247
  activeBlock === id ? false : isMultipleSelection,
247
248
  e,
248
249
  );
@@ -1,5 +1,4 @@
1
- import React from 'react';
2
- import { connect } from 'react-redux';
1
+ import React, { useState, useEffect } from 'react';
3
2
  import { compose } from 'redux';
4
3
  import { withRouter } from 'react-router';
5
4
  import cx from 'classnames';
@@ -15,11 +14,13 @@ import {
15
14
 
16
15
  import '@eeacms/volto-tabs-block/less/menu.less';
17
16
 
17
+ import noop from 'lodash/noop';
18
+
18
19
  const MenuItem = (props) => {
19
20
  const {
20
21
  activeTab = null,
21
22
  tabs = {},
22
- setActiveTab = () => {},
23
+ setActiveTab = noop,
23
24
  tabsTitle,
24
25
  tabsDescription,
25
26
  blockId,
@@ -28,8 +29,22 @@ const MenuItem = (props) => {
28
29
  const { tab, index } = props;
29
30
  const title = tabs[tab].title;
30
31
  const tabIndex = index + 1;
32
+
33
+ const [tabChanged, setTabChanged] = useState(false);
31
34
  const defaultTitle = `Tab ${tabIndex}`;
32
35
 
36
+ useEffect(() => {
37
+ if (
38
+ tabChanged === true &&
39
+ document?.getElementById(blockId)?.querySelector('#tab-pane-' + tab)
40
+ ) {
41
+ document
42
+ .getElementById(blockId)
43
+ .querySelector('#tab-pane-' + tab)
44
+ .focus();
45
+ setTabChanged(false);
46
+ }
47
+ }, [tabChanged, tab, blockId]);
33
48
  return (
34
49
  <React.Fragment>
35
50
  {index === 0 && (tabsTitle || tabsDescription) && (
@@ -50,19 +65,10 @@ const MenuItem = (props) => {
50
65
  onKeyDown={(e) => {
51
66
  if (e.key === 'Enter') {
52
67
  e.preventDefault();
53
- if (
54
- document
55
- .getElementById(blockId)
56
- ?.getElementsByClassName('active tab').length > 0
57
- ) {
58
- const tabDiv = document
59
- .getElementById(blockId)
60
- .getElementsByClassName('active tab')[0];
61
- tabDiv.focus();
62
- }
63
68
  if (activeTab !== tab) {
64
69
  setActiveTab(tab);
65
70
  }
71
+ setTabChanged(true);
66
72
  }
67
73
  }}
68
74
  >
@@ -74,15 +80,12 @@ const MenuItem = (props) => {
74
80
  };
75
81
 
76
82
  const View = (props) => {
77
- const [hashlinkOnMount, setHashlinkOnMount] = React.useState(false);
78
83
  const {
79
84
  metadata = {},
80
85
  data = {},
81
86
  tabsList = [],
82
87
  tabs = {},
83
88
  activeTabIndex = 0,
84
- hashlink = {},
85
- setActiveTab = () => {},
86
89
  } = props;
87
90
  const [menuPosition, setMenuPosition] = React.useState({});
88
91
 
@@ -115,33 +118,6 @@ const View = (props) => {
115
118
  [schema, data],
116
119
  );
117
120
 
118
- React.useEffect(() => {
119
- const urlHash = props.location.hash.substring(1) || '';
120
- if (
121
- hashlink.counter > 0 ||
122
- (hashlink.counter === 0 && urlHash && !hashlinkOnMount)
123
- ) {
124
- const id = hashlink.hash || urlHash || '';
125
- const index = tabsList.indexOf(id);
126
- const parentId = data.id || props.id;
127
- const parent = document.getElementById(parentId);
128
- const headerWrapper = document.querySelector('.header-wrapper');
129
- const offsetHeight = headerWrapper?.offsetHeight || 0;
130
- if (id !== parentId && index > -1 && parent) {
131
- if (activeTabIndex !== index) {
132
- setActiveTab(id);
133
- }
134
- props.scrollToTarget(parent, offsetHeight);
135
- } else if (id === parentId && parent) {
136
- props.scrollToTarget(parent, offsetHeight);
137
- }
138
- }
139
- if (!hashlinkOnMount) {
140
- setHashlinkOnMount(true);
141
- }
142
- /* eslint-disable-next-line */
143
- }, [hashlink.counter]);
144
-
145
121
  const panes = tabsList.map((tab, index) => {
146
122
  return {
147
123
  id: tab,
@@ -156,13 +132,13 @@ const View = (props) => {
156
132
  blockId={props?.id || ''}
157
133
  />
158
134
  ),
159
- render: () => {
160
- return (
161
- <Tab.Pane as={isContainer ? Container : undefined} tabIndex={0}>
135
+ pane: (
136
+ <Tab.Pane as={isContainer ? Container : undefined}>
137
+ <div tabIndex={0} role="tabpanel" id={'tab-pane-' + tab}>
162
138
  <RenderBlocks {...props} metadata={metadata} content={tabs[tab]} />
163
- </Tab.Pane>
164
- );
165
- },
139
+ </div>
140
+ </Tab.Pane>
141
+ ),
166
142
  };
167
143
  });
168
144
 
@@ -171,6 +147,7 @@ const View = (props) => {
171
147
  <Tab
172
148
  activeIndex={activeTabIndex}
173
149
  className="default tabs tabs-accessibility"
150
+ renderActiveOnly={false}
174
151
  menu={{
175
152
  attached: menuPosition.attached,
176
153
  borderless: getDataValue('menuBorderless'),
@@ -202,11 +179,4 @@ const View = (props) => {
202
179
  );
203
180
  };
204
181
 
205
- export default compose(
206
- connect((state) => {
207
- return {
208
- hashlink: state.hashlink,
209
- };
210
- }),
211
- withScrollToTarget,
212
- )(withRouter(View));
182
+ export default compose(withScrollToTarget)(withRouter(View));
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useState, useEffect } from 'react';
2
2
  import { connect } from 'react-redux';
3
3
  import { compose } from 'redux';
4
4
  import { withRouter } from 'react-router';
@@ -7,7 +7,6 @@ import { Menu, Tab, Container, Dropdown, Button } from 'semantic-ui-react';
7
7
  import config from '@plone/volto/registry';
8
8
  import { RenderBlocks } from '@plone/volto/components';
9
9
  import { TABS_BLOCK } from '@eeacms/volto-tabs-block/constants';
10
- import { withScrollToTarget } from '@eeacms/volto-tabs-block/hocs';
11
10
  import {
12
11
  SimpleMarkdown,
13
12
  getMenuPosition,
@@ -17,18 +16,27 @@ import {
17
16
 
18
17
  import '@eeacms/volto-tabs-block/less/menu.less';
19
18
 
19
+ import noop from 'lodash/noop';
20
+
20
21
  const MenuItem = (props) => {
21
- const {
22
- activeTab = null,
23
- tabs = {},
24
- setActiveTab = () => {},
25
- blockId,
26
- } = props;
22
+ const { activeTab = null, tabs = {}, setActiveTab = noop, blockId } = props;
27
23
  const { tabsTitle, tabsDescription, tab, index } = props;
28
24
  const title = tabs[tab].title;
29
25
  const tabIndex = index + 1;
30
26
  const defaultTitle = `Tab ${tabIndex}`;
31
-
27
+ const [tabChanged, setTabChanged] = useState(false);
28
+ useEffect(() => {
29
+ if (
30
+ tabChanged === true &&
31
+ document?.getElementById(blockId)?.querySelector('#tab-pane-' + tab)
32
+ ) {
33
+ document
34
+ .getElementById(blockId)
35
+ .querySelector('#tab-pane-' + tab)
36
+ .focus();
37
+ setTabChanged(false);
38
+ }
39
+ }, [tabChanged, tab, blockId]);
32
40
  return (
33
41
  <React.Fragment>
34
42
  {index === 0 && (tabsTitle || tabsDescription) && (
@@ -73,19 +81,10 @@ const MenuItem = (props) => {
73
81
  onKeyDown={(e) => {
74
82
  if (e.key === 'Enter') {
75
83
  e.preventDefault();
76
- if (
77
- document
78
- .getElementById(blockId)
79
- ?.getElementsByClassName('active tab').length > 0
80
- ) {
81
- const tabDiv = document
82
- .getElementById(blockId)
83
- .getElementsByClassName('active tab')[0];
84
- tabDiv.focus();
85
- }
86
84
  if (activeTab !== tab) {
87
85
  setActiveTab(tab);
88
86
  }
87
+ setTabChanged(true);
89
88
  }
90
89
  }}
91
90
  >
@@ -105,12 +104,12 @@ const MenuWrapper = (props) => {
105
104
  screen = {},
106
105
  tabs = {},
107
106
  tabsList = [],
108
- setActiveTab = () => {},
107
+ setActiveTab = noop,
109
108
  } = props;
110
109
  const [open, setOpen] = React.useState(false);
111
110
 
112
111
  React.useEffect(() => {
113
- if (!true || !node?.current) return;
112
+ if (false || !node?.current) return;
114
113
  const items = node.current.querySelectorAll(
115
114
  '.ui.menu > .menu-wrapper > .item:not(.menu-title)',
116
115
  );
@@ -182,16 +181,13 @@ const MenuWrapper = (props) => {
182
181
  };
183
182
 
184
183
  const View = (props) => {
185
- const [hashlinkOnMount, setHashlinkOnMount] = React.useState(false);
186
184
  const {
187
185
  metadata = {},
188
186
  data = {},
189
187
  tabsList = [],
190
188
  tabs = {},
191
189
  activeTabIndex = 0,
192
- hashlink = {},
193
190
  screen,
194
- setActiveTab = () => {},
195
191
  } = props;
196
192
  const menuPosition = getMenuPosition(data);
197
193
  const isContainer = data.align === 'full';
@@ -217,33 +213,6 @@ const View = (props) => {
217
213
  [schema, data],
218
214
  );
219
215
 
220
- React.useEffect(() => {
221
- const urlHash = props.location.hash.substring(1) || '';
222
- if (
223
- hashlink.counter > 0 ||
224
- (hashlink.counter === 0 && urlHash && !hashlinkOnMount)
225
- ) {
226
- const id = hashlink.hash || urlHash || '';
227
- const index = tabsList.indexOf(id);
228
- const parentId = data.id || props.id;
229
- const parent = document.getElementById(parentId);
230
- const headerWrapper = document.querySelector('.header-wrapper');
231
- const offsetHeight = headerWrapper?.offsetHeight || 0;
232
- if (id !== parentId && index > -1 && parent) {
233
- if (activeTabIndex !== index) {
234
- setActiveTab(id);
235
- }
236
- props.scrollToTarget(parent, offsetHeight);
237
- } else if (id === parentId && parent) {
238
- props.scrollToTarget(parent, offsetHeight);
239
- }
240
- }
241
- if (!hashlinkOnMount) {
242
- setHashlinkOnMount(true);
243
- }
244
- /* eslint-disable-next-line */
245
- }, [hashlink.counter]);
246
-
247
216
  const panes = tabsList.map((tab, index) => {
248
217
  return {
249
218
  id: tab,
@@ -260,19 +229,13 @@ const View = (props) => {
260
229
  tabsDescription={tabsDescription}
261
230
  />
262
231
  ),
263
- render: () => {
264
- return (
265
- <>
266
- <Tab.Pane as={isContainer ? Container : undefined} tabIndex={0}>
267
- <RenderBlocks
268
- {...props}
269
- metadata={metadata}
270
- content={tabs[tab]}
271
- />
272
- </Tab.Pane>
273
- </>
274
- );
275
- },
232
+ pane: (
233
+ <Tab.Pane as={isContainer ? Container : undefined}>
234
+ <div tabIndex={0} role="tabpanel" id={'tab-pane-' + tab}>
235
+ <RenderBlocks {...props} metadata={metadata} content={tabs[tab]} />
236
+ </div>
237
+ </Tab.Pane>
238
+ ),
276
239
  };
277
240
  });
278
241
 
@@ -281,6 +244,7 @@ const View = (props) => {
281
244
  <Tab
282
245
  activeIndex={activeTabIndex}
283
246
  className="horizontal-responsive tabs"
247
+ renderActiveOnly={false}
284
248
  menu={{
285
249
  attached: menuPosition.attached,
286
250
  borderless: getDataValue('menuBorderless'),
@@ -316,9 +280,7 @@ const View = (props) => {
316
280
  export default compose(
317
281
  connect((state) => {
318
282
  return {
319
- hashlink: state.hashlink,
320
283
  screen: state.screen,
321
284
  };
322
285
  }),
323
- withScrollToTarget,
324
286
  )(withRouter(View));
package/src/helpers.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { v4 as uuid } from 'uuid';
2
2
  import { emptyBlocksForm } from '@plone/volto/helpers';
3
+ import { visitBlocks, toSlug } from '@eeacms/volto-anchors/helpers';
3
4
 
4
5
  export const empty = () => {
5
6
  const tabId = uuid();
@@ -34,3 +35,20 @@ export const scrollToTarget = (target, offsetHeight = 0) => {
34
35
 
35
36
  return;
36
37
  };
38
+
39
+ export const getParentTabFromHash = (tabsBlockData, urlHash) => {
40
+ if (urlHash) {
41
+ const { data } = tabsBlockData;
42
+ let parentBlockId;
43
+ let isSlugPresent;
44
+ visitBlocks(data, ([id, data]) => {
45
+ if (data['@type'] === 'tab') parentBlockId = id;
46
+ if (toSlug(data.plaintext) === urlHash) {
47
+ isSlugPresent = true;
48
+ return true;
49
+ }
50
+ });
51
+ return isSlugPresent ? parentBlockId : null;
52
+ }
53
+ return null;
54
+ };
package/src/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import { TabsEdit, TabsView } from '@eeacms/volto-tabs-block/components';
2
1
  import {
3
2
  DefaultEdit,
4
3
  DefaultView,
@@ -11,8 +10,10 @@ import {
11
10
  accordionSchema,
12
11
  horizontalResponsiveSchema,
13
12
  carouselSchema,
13
+ layoutSchema,
14
+ TabsEdit,
15
+ TabsView,
14
16
  } from '@eeacms/volto-tabs-block/components';
15
- import { layoutSchema } from '@eeacms/volto-tabs-block/components';
16
17
  import { TABS_BLOCK } from './constants';
17
18
  import { TabsWidget } from './widgets';
18
19
 
@@ -94,10 +95,5 @@ export default (config) => {
94
95
 
95
96
  config.widgets.type.tabs = TabsWidget;
96
97
 
97
- config.settings.hashlinkBlacklist = [
98
- ...(config.settings.hashlinkBlacklist || []),
99
- TABS_BLOCK,
100
- ];
101
-
102
98
  return config;
103
99
  };
@@ -49,8 +49,8 @@
49
49
  padding-bottom: @verticalPadding;
50
50
  }
51
51
 
52
- > .styled > .default.tabs,
53
- > .default.tabs {
52
+ > .styled > .default.tabs.active,
53
+ > .default.tabs.active {
54
54
  display: flex;
55
55
  flex-direction: column;
56
56
 
@@ -2,11 +2,9 @@ import React from 'react';
2
2
  import { v4 as uuid } from 'uuid';
3
3
  import { omit, without } from 'lodash';
4
4
  import move from 'lodash-move';
5
- import { Icon, FormFieldWrapper } from '@plone/volto/components';
6
- import { DragDropList } from '@plone/volto/components';
5
+ import { Icon, FormFieldWrapper, DragDropList } from '@plone/volto/components';
7
6
  import { emptyTab } from '@eeacms/volto-tabs-block/helpers';
8
7
  import { StyleWrapperEdit } from '@eeacms/volto-block-style/StyleWrapper';
9
-
10
8
  import dragSVG from '@plone/volto/icons/drag.svg';
11
9
  import themeSVG from '@plone/volto/icons/theme.svg';
12
10
  import trashSVG from '@plone/volto/icons/delete.svg';
@@ -30,9 +28,9 @@ const TabsWidget = (props) => {
30
28
  const [activeTabId, setActiveTabId] = React.useState(0);
31
29
  const { value = {}, id, onChange } = props;
32
30
  const { blocks = {} } = value;
33
- const tabsList = (value.blocks_layout?.items || []).map((id) => [
34
- id,
35
- blocks[id],
31
+ const tabsList = (value.blocks_layout?.items || []).map((uid) => [
32
+ uid,
33
+ blocks[uid],
36
34
  ]);
37
35
  const activeTabData = blocks[activeTabId] || {};
38
36