@comicrelief/component-library 8.52.1 → 8.52.2
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/dist/components/Organisms/DynamicGallery/DynamicGallery.js +9 -3
- package/dist/components/Organisms/DynamicGallery/DynamicGallery.style.js +13 -16
- package/dist/components/Organisms/DynamicGallery/_DynamicGalleryColumn.js +37 -28
- package/dist/components/Organisms/DynamicGallery/_Lightbox.js +14 -18
- package/dist/components/Organisms/DynamicGallery/__snapshots__/DynamicGallery.test.js.snap +64 -111
- package/dist/components/Organisms/DynamicGallery/_types.js +4 -3
- package/dist/components/Organisms/DynamicGallery/_utils.js +29 -3
- package/package.json +1 -1
- package/src/components/Organisms/DynamicGallery/DynamicGallery.js +8 -2
- package/src/components/Organisms/DynamicGallery/DynamicGallery.style.js +7 -15
- package/src/components/Organisms/DynamicGallery/_DynamicGalleryColumn.js +49 -41
- package/src/components/Organisms/DynamicGallery/_Lightbox.js +21 -26
- package/src/components/Organisms/DynamicGallery/__snapshots__/DynamicGallery.test.js.snap +64 -111
- package/src/components/Organisms/DynamicGallery/_types.js +4 -3
- package/src/components/Organisms/DynamicGallery/_utils.js +40 -3
|
@@ -103,6 +103,7 @@ const DynamicGallery = _ref => {
|
|
|
103
103
|
|
|
104
104
|
// handle selected gallery node
|
|
105
105
|
const [selectedNode, setSelectedNode] = (0, _react.useState)(null);
|
|
106
|
+
const [focusedNode, setFocusedNode] = (0, _react.useState)(null);
|
|
106
107
|
|
|
107
108
|
// handle next/previous node events from the lightbox
|
|
108
109
|
function handleNextNode(node) {
|
|
@@ -128,6 +129,8 @@ const DynamicGallery = _ref => {
|
|
|
128
129
|
const nodeIndex = +event.target.dataset.nodeIndex;
|
|
129
130
|
if (Number.isNaN(nodeIndex)) return;
|
|
130
131
|
setSelectedNode(nodes[nodeIndex]);
|
|
132
|
+
// also store the focused node for focus restoration when the lightbox closes
|
|
133
|
+
setFocusedNode(event.target.closest('.gallery-node'));
|
|
131
134
|
}
|
|
132
135
|
break;
|
|
133
136
|
}
|
|
@@ -190,7 +193,9 @@ const DynamicGallery = _ref => {
|
|
|
190
193
|
selectedNode,
|
|
191
194
|
setSelectedNode,
|
|
192
195
|
nextNode: handleNextNode,
|
|
193
|
-
previousNode: handlePreviousNode
|
|
196
|
+
previousNode: handlePreviousNode,
|
|
197
|
+
focusedNode,
|
|
198
|
+
setFocusedNode
|
|
194
199
|
}
|
|
195
200
|
}, /*#__PURE__*/_react.default.createElement(_DynamicGallery.ImageGrid, {
|
|
196
201
|
className: "gallery-grid",
|
|
@@ -205,7 +210,8 @@ const DynamicGallery = _ref => {
|
|
|
205
210
|
columnCount: columnCount,
|
|
206
211
|
nodes: nodes.slice(0, imageCount),
|
|
207
212
|
imageRatio: imageRatio,
|
|
208
|
-
updateTabOrder: throttledUpdateTabOrder.current
|
|
213
|
+
updateTabOrder: throttledUpdateTabOrder.current,
|
|
214
|
+
focusOutlineColour: textColour
|
|
209
215
|
})), /*#__PURE__*/_react.default.createElement(_DynamicGallery.EmptyMessage, {
|
|
210
216
|
isEmpty: !hasNodes
|
|
211
217
|
}, "No images to display")), /*#__PURE__*/_react.default.createElement(_Lightbox.default, null), /*#__PURE__*/_react.default.createElement("div", {
|
|
@@ -213,6 +219,6 @@ const DynamicGallery = _ref => {
|
|
|
213
219
|
tabIndex: 0
|
|
214
220
|
})), imageCount < nodes.length && /*#__PURE__*/_react.default.createElement(_Button.default, {
|
|
215
221
|
onClick: () => handleLoadMore()
|
|
216
|
-
}, "
|
|
222
|
+
}, "Show more"));
|
|
217
223
|
};
|
|
218
224
|
var _default = exports.default = DynamicGallery;
|
|
@@ -4,7 +4,7 @@ var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWild
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.
|
|
7
|
+
exports.InteractiveGalleryNode = exports.ImageGrid = exports.ImageContainer = exports.GalleryNode = exports.EmptyMessage = exports.Details = exports.Container = exports.Column = void 0;
|
|
8
8
|
var _styledComponents = _interopRequireWildcard(require("styled-components"));
|
|
9
9
|
const Container = exports.Container = _styledComponents.default.div.withConfig({
|
|
10
10
|
displayName: "DynamicGallerystyle__Container",
|
|
@@ -60,7 +60,12 @@ const EmptyMessage = exports.EmptyMessage = _styledComponents.default.div.withCo
|
|
|
60
60
|
} = _ref7;
|
|
61
61
|
return isEmpty ? 'block' : 'none';
|
|
62
62
|
});
|
|
63
|
-
const GalleryNodeBase = (0, _styledComponents.css)(["display:flex;flex-direction:column;gap:0.
|
|
63
|
+
const GalleryNodeBase = (0, _styledComponents.css)(["display:flex;flex-direction:column;gap:0.9rem;padding:0;margin:0;background:none;border:none;text-align:left;&:focus-visible{outline:2px solid ", ";outline-offset:0.5rem;border-radius:1rem;}"], _ref8 => {
|
|
64
|
+
let {
|
|
65
|
+
focusOutlineColour
|
|
66
|
+
} = _ref8;
|
|
67
|
+
return focusOutlineColour;
|
|
68
|
+
});
|
|
64
69
|
const GalleryNode = exports.GalleryNode = _styledComponents.default.div.withConfig({
|
|
65
70
|
displayName: "DynamicGallerystyle__GalleryNode",
|
|
66
71
|
componentId: "sc-1kgt7yr-4"
|
|
@@ -68,30 +73,22 @@ const GalleryNode = exports.GalleryNode = _styledComponents.default.div.withConf
|
|
|
68
73
|
const InteractiveGalleryNode = exports.InteractiveGalleryNode = _styledComponents.default.button.withConfig({
|
|
69
74
|
displayName: "DynamicGallerystyle__InteractiveGalleryNode",
|
|
70
75
|
componentId: "sc-1kgt7yr-5"
|
|
71
|
-
})(["", " cursor:pointer;color:inherit;& div:first-child{transition:all 0.1s ease-out;}
|
|
76
|
+
})(["", " cursor:pointer;color:inherit;& div:first-child{transition:all 0.1s ease-out;}& > div:first-child{&:hover{box-shadow:0px 3px 10px 0px rgba(0,0,0,0.4);}}"], GalleryNodeBase);
|
|
72
77
|
const ImageContainer = exports.ImageContainer = _styledComponents.default.div.withConfig({
|
|
73
78
|
displayName: "DynamicGallerystyle__ImageContainer",
|
|
74
79
|
componentId: "sc-1kgt7yr-6"
|
|
75
|
-
})(["display:flex;height:auto;width:100%;min-height:", ";max-height:", ";overflow:hidden;border-radius:1rem;background:rgba(0,0,0,0.05);box-shadow:0px 2px 8px 0px rgba(0,0,0,0.2);img{height:100%;opacity:0;transition:opacity 0.1s ease-out 0.3s;}"],
|
|
80
|
+
})(["display:flex;height:auto;width:100%;min-height:", ";max-height:", ";overflow:hidden;border-radius:1rem;background:rgba(0,0,0,0.05);box-shadow:0px 2px 8px 0px rgba(0,0,0,0.2);img{height:100%;opacity:0;transition:opacity 0.1s ease-out 0.3s;}"], _ref9 => {
|
|
76
81
|
let {
|
|
77
82
|
minHeight
|
|
78
|
-
} =
|
|
83
|
+
} = _ref9;
|
|
79
84
|
return minHeight;
|
|
80
|
-
},
|
|
85
|
+
}, _ref10 => {
|
|
81
86
|
let {
|
|
82
87
|
maxHeight
|
|
83
|
-
} =
|
|
88
|
+
} = _ref10;
|
|
84
89
|
return maxHeight;
|
|
85
90
|
});
|
|
86
91
|
const Details = exports.Details = _styledComponents.default.div.withConfig({
|
|
87
92
|
displayName: "DynamicGallerystyle__Details",
|
|
88
93
|
componentId: "sc-1kgt7yr-7"
|
|
89
|
-
})(["display:flex;flex-direction:column;gap:0.5rem;padding:0 1rem;"]);
|
|
90
|
-
const Title = exports.Title = _styledComponents.default.div.withConfig({
|
|
91
|
-
displayName: "DynamicGallerystyle__Title",
|
|
92
|
-
componentId: "sc-1kgt7yr-8"
|
|
93
|
-
})(["&:first-child{margin-bottom:0;}"]);
|
|
94
|
-
const Caption = exports.Caption = _styledComponents.default.div.withConfig({
|
|
95
|
-
displayName: "DynamicGallerystyle__Caption",
|
|
96
|
-
componentId: "sc-1kgt7yr-9"
|
|
97
|
-
})(["line-height:1;"]);
|
|
94
|
+
})(["display:flex;flex-direction:column;gap:0.5rem;padding:0 1rem;"]);
|
|
@@ -12,12 +12,14 @@ var _Picture = _interopRequireDefault(require("../../Atoms/Picture/Picture"));
|
|
|
12
12
|
var _Lightbox = require("./_Lightbox");
|
|
13
13
|
var _DynamicGallery = require("./DynamicGallery.style");
|
|
14
14
|
var _types = require("./_types");
|
|
15
|
+
var _utils = require("./_utils");
|
|
15
16
|
/**
|
|
16
17
|
* a separate component to handle columns of images;
|
|
17
18
|
* this component handles aspect ratio calculations to enfore a min/max ratio for its images
|
|
18
19
|
*/
|
|
19
20
|
function DynamicGalleryColumn(_ref) {
|
|
20
21
|
let {
|
|
22
|
+
focusOutlineColour,
|
|
21
23
|
updateTabOrder,
|
|
22
24
|
nodes,
|
|
23
25
|
imageRatio,
|
|
@@ -79,33 +81,40 @@ function DynamicGalleryColumn(_ref) {
|
|
|
79
81
|
return /*#__PURE__*/_react.default.createElement(_DynamicGallery.Column, {
|
|
80
82
|
ref: elRef,
|
|
81
83
|
className: "gallery-column"
|
|
82
|
-
}, nodes === null || nodes === void 0 ? void 0 : nodes.filter((_, nodeIndex) => nodeIndex % columnCount === columnIndex).map((node, nodeIndex) =>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
84
|
+
}, nodes === null || nodes === void 0 ? void 0 : nodes.filter((_, nodeIndex) => nodeIndex % columnCount === columnIndex).map((node, nodeIndex) => {
|
|
85
|
+
const bodyText = (0, _utils.extractNodeText)(node.gridBody);
|
|
86
|
+
const key = String(nodeIndex) + bodyText;
|
|
87
|
+
return /*#__PURE__*/_react.default.createElement(NodeComponent, {
|
|
88
|
+
key: key,
|
|
89
|
+
className: "gallery-node",
|
|
90
|
+
caption: bodyText,
|
|
91
|
+
"aria-label": bodyText,
|
|
92
|
+
title: bodyText,
|
|
93
|
+
"data-node-index": nodeIndex,
|
|
94
|
+
focusOutlineColour: focusOutlineColour,
|
|
95
|
+
onPointerUp: useLightbox ? () => handlePointerUp(node) : undefined,
|
|
96
|
+
tabIndex: 0
|
|
97
|
+
}, /*#__PURE__*/_react.default.createElement(_DynamicGallery.ImageContainer, {
|
|
98
|
+
className: "gallery-node-image"
|
|
99
|
+
// eslint prefers template literals for strings, but they break the compiler
|
|
100
|
+
// eslint-disable-next-line prefer-template
|
|
101
|
+
,
|
|
102
|
+
minHeight: String(minHeight) + 'px'
|
|
103
|
+
// eslint-disable-next-line prefer-template
|
|
104
|
+
,
|
|
105
|
+
maxHeight: String(maxHeight) + 'px'
|
|
106
|
+
}, /*#__PURE__*/_react.default.createElement(_Picture.default, {
|
|
107
|
+
image: node.image,
|
|
108
|
+
objectFit: "cover",
|
|
109
|
+
alt: bodyText
|
|
110
|
+
// animate image in on load
|
|
111
|
+
,
|
|
112
|
+
onLoad: event => {
|
|
113
|
+
event.target.closest('.gallery-node-image').querySelector('img').style.setProperty('opacity', '1');
|
|
106
114
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
115
|
+
// update tab order once the image has loaded
|
|
116
|
+
updateTabOrder();
|
|
117
|
+
}
|
|
118
|
+
})), /*#__PURE__*/_react.default.createElement(_DynamicGallery.Details, null, node.gridBody && /*#__PURE__*/_react.default.createElement("div", null, node.gridBody), node.gridCaption && /*#__PURE__*/_react.default.createElement("div", null, node.gridCaption)));
|
|
119
|
+
}));
|
|
111
120
|
}
|
|
@@ -13,6 +13,7 @@ var _Cross = _interopRequireDefault(require("../../Atoms/Icons/Cross"));
|
|
|
13
13
|
var _Picture = _interopRequireDefault(require("../../Atoms/Picture/Picture"));
|
|
14
14
|
var _Lightbox = require("./_Lightbox.style");
|
|
15
15
|
var _ScrollFix = _interopRequireDefault(require("./_ScrollFix"));
|
|
16
|
+
var _utils = require("./_utils");
|
|
16
17
|
/**
|
|
17
18
|
* lightbox context:
|
|
18
19
|
* - selectedNode: the node that is currently selected
|
|
@@ -44,11 +45,12 @@ const Lightbox = () => {
|
|
|
44
45
|
selectedNode,
|
|
45
46
|
setSelectedNode,
|
|
46
47
|
nextNode,
|
|
47
|
-
previousNode
|
|
48
|
+
previousNode,
|
|
49
|
+
focusedNode,
|
|
50
|
+
setFocusedNode
|
|
48
51
|
} = (0, _react.useContext)(LightboxContext);
|
|
49
52
|
const hasNode = Boolean(selectedNode);
|
|
50
53
|
const dialogRef = (0, _react.useRef)(null);
|
|
51
|
-
const previousFocusRef = (0, _react.useRef)(null);
|
|
52
54
|
|
|
53
55
|
/**
|
|
54
56
|
* handle keyboard events within the lightbox;
|
|
@@ -104,27 +106,20 @@ const Lightbox = () => {
|
|
|
104
106
|
|
|
105
107
|
// handle focus management when dialog opens/closes
|
|
106
108
|
(0, _react.useEffect)(() => {
|
|
107
|
-
// when the lightbox is opened, store the previously focused element
|
|
108
|
-
// and move focus to the first focusable element in the dialog
|
|
109
109
|
if (hasNode) {
|
|
110
|
-
//
|
|
111
|
-
previousFocusRef.current = document.activeElement;
|
|
112
|
-
// move focus to the first focusable element in the dialog
|
|
110
|
+
// move focus to the first focusable element in the dialog when it opens
|
|
113
111
|
setTimeout(() => {
|
|
114
112
|
const focusableElements = getFocusableElements(dialogRef.current);
|
|
115
113
|
if (focusableElements.length > 0) {
|
|
116
114
|
focusableElements[0].focus();
|
|
117
115
|
}
|
|
118
116
|
}, 0);
|
|
119
|
-
|
|
117
|
+
} else {
|
|
118
|
+
// restore focus to the previously focused element when lightbox closes
|
|
119
|
+
focusedNode === null || focusedNode === void 0 ? void 0 : focusedNode.focus();
|
|
120
|
+
setFocusedNode(null);
|
|
120
121
|
}
|
|
121
|
-
|
|
122
|
-
// when the lightbox is closed, restore focus to the previously focused element
|
|
123
|
-
if (previousFocusRef.current && typeof previousFocusRef.current.focus === 'function') {
|
|
124
|
-
previousFocusRef.current.focus();
|
|
125
|
-
previousFocusRef.current = null;
|
|
126
|
-
}
|
|
127
|
-
}, [hasNode]);
|
|
122
|
+
}, [hasNode, focusedNode, setFocusedNode]);
|
|
128
123
|
|
|
129
124
|
/**
|
|
130
125
|
* close the lightbox when the backdrop is clicked
|
|
@@ -165,6 +160,7 @@ const Lightbox = () => {
|
|
|
165
160
|
});
|
|
166
161
|
target.style.opacity = '1';
|
|
167
162
|
}
|
|
163
|
+
const bodyText = (0, _utils.extractNodeText)(selectedNode === null || selectedNode === void 0 ? void 0 : selectedNode.lightboxBody);
|
|
168
164
|
return /*#__PURE__*/_react.default.createElement(_Lightbox.Container, {
|
|
169
165
|
isOpen: hasNode
|
|
170
166
|
}, /*#__PURE__*/_react.default.createElement(_Lightbox.Backdrop, {
|
|
@@ -181,7 +177,7 @@ const Lightbox = () => {
|
|
|
181
177
|
color: "#E1E2E3"
|
|
182
178
|
})), hasNode && /*#__PURE__*/_react.default.createElement(_Picture.default, {
|
|
183
179
|
key: selectedNode === null || selectedNode === void 0 ? void 0 : selectedNode.image,
|
|
184
|
-
alt:
|
|
180
|
+
alt: bodyText,
|
|
185
181
|
image: selectedNode === null || selectedNode === void 0 ? void 0 : selectedNode.image,
|
|
186
182
|
width: imageDimensions.width,
|
|
187
183
|
height: imageDimensions.height,
|
|
@@ -191,9 +187,9 @@ const Lightbox = () => {
|
|
|
191
187
|
id: "lightboxDescription",
|
|
192
188
|
"aria-live": "polite",
|
|
193
189
|
"aria-atomic": "true"
|
|
194
|
-
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
190
|
+
}, (selectedNode === null || selectedNode === void 0 ? void 0 : selectedNode.lightboxBody) && /*#__PURE__*/_react.default.createElement("div", {
|
|
195
191
|
id: "lightboxTitle"
|
|
196
|
-
}, selectedNode
|
|
192
|
+
}, selectedNode.lightboxBody), (selectedNode === null || selectedNode === void 0 ? void 0 : selectedNode.lightboxCaption) && /*#__PURE__*/_react.default.createElement("div", null, selectedNode === null || selectedNode === void 0 ? void 0 : selectedNode.lightboxCaption)), /*#__PURE__*/_react.default.createElement(_Lightbox.CloseButton, {
|
|
197
193
|
type: "button",
|
|
198
194
|
onClick: () => setSelectedNode(null)
|
|
199
195
|
}, /*#__PURE__*/_react.default.createElement(_Lightbox.ScreenReaderOnly, null, "Close"), /*#__PURE__*/_react.default.createElement(_Cross.default, {
|