@openstack_dev/gatsby-theme-marketing-oif-core 1.0.0

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 (93) hide show
  1. package/.eslintrc.json +27 -0
  2. package/.github/workflows/eslint.yml +15 -0
  3. package/.husky/pre-commit +4 -0
  4. package/.nvmrc +1 -0
  5. package/LICENSE.md +201 -0
  6. package/README.md +10 -0
  7. package/babel.config.json +12 -0
  8. package/gatsby-browser.js +5 -0
  9. package/gatsby-config.js +217 -0
  10. package/gatsby-node.js +162 -0
  11. package/gatsby-ssr.js +50 -0
  12. package/package.json +154 -0
  13. package/src/cms/cms-utils.js +8 -0
  14. package/src/cms/cms.js +12 -0
  15. package/src/cms/config/collections/configurationsCollection/announcementBanner/index.js +59 -0
  16. package/src/cms/config/collections/configurationsCollection/announcementBanner/typeDefs.js +11 -0
  17. package/src/cms/config/collections/configurationsCollection/footer/index.js +158 -0
  18. package/src/cms/config/collections/configurationsCollection/footer/typeDefs.js +37 -0
  19. package/src/cms/config/collections/configurationsCollection/index.js +16 -0
  20. package/src/cms/config/collections/configurationsCollection/navbar/index.js +62 -0
  21. package/src/cms/config/collections/configurationsCollection/navbar/typeDefs.js +13 -0
  22. package/src/cms/config/collections/configurationsCollection/siteSettings/index.js +117 -0
  23. package/src/cms/config/collections/configurationsCollection/siteSettings/typeDefs.js +15 -0
  24. package/src/cms/config/collections/configurationsCollection/typeDefs.js +9 -0
  25. package/src/cms/config/collections/typeDefs.js +5 -0
  26. package/src/cms/config/fields.js +268 -0
  27. package/src/cms/config/index.js +35 -0
  28. package/src/cms/config/patterns.js +51 -0
  29. package/src/cms/preview-templates/.gitkeep +0 -0
  30. package/src/cms/widgets/.gitkeep +0 -0
  31. package/src/components/AnnouncementBanner/index.js +26 -0
  32. package/src/components/AnnouncementBanner/index.module.scss +131 -0
  33. package/src/components/AnnouncementBanner/template.js +40 -0
  34. package/src/components/Footer/index.js +40 -0
  35. package/src/components/Footer/index.module.scss +59 -0
  36. package/src/components/Footer/template.js +55 -0
  37. package/src/components/Header/index.js +8 -0
  38. package/src/components/Header/template.js +5 -0
  39. package/src/components/Layout.js +32 -0
  40. package/src/components/Link.js +41 -0
  41. package/src/components/Navbar/index.js +460 -0
  42. package/src/components/Navbar/index.module.scss +301 -0
  43. package/src/components/SponsoredProjectsNav/index.js +22 -0
  44. package/src/components/SponsoredProjectsNav/index.module.scss +7 -0
  45. package/src/components/SubscribeForm/index.js +47 -0
  46. package/src/components/SubscribeForm/index.module.scss +114 -0
  47. package/src/components/Tracking/custom-bing-tracker.js +5 -0
  48. package/src/components/Tracking/custom-google-tracker.js +51 -0
  49. package/src/components/head-components.js +20 -0
  50. package/src/components/svgs/RightArrow.jsx +9 -0
  51. package/src/content/announcement-banner/OpenInfrastructureFoundation-icon-RGB.svg +1 -0
  52. package/src/content/announcement-banner/index.json +8 -0
  53. package/src/content/footer/index.json +139 -0
  54. package/src/content/navbar/index.json +304 -0
  55. package/src/content/site-settings/index.json +1 -0
  56. package/src/images/icon.png +0 -0
  57. package/src/images/openstack-logo-full.svg +57 -0
  58. package/src/images/openstack-logo-vert.svg +57 -0
  59. package/src/images/right-arrow.svg +3 -0
  60. package/src/pages/404.js +49 -0
  61. package/src/pages/auth/[...].js +36 -0
  62. package/src/pages/index.js +14 -0
  63. package/src/reducers/.gitkeep +0 -0
  64. package/src/reducers/index.js +5 -0
  65. package/src/routes/.gitkeep +0 -0
  66. package/src/routes/authorization-callback-route.js +71 -0
  67. package/src/routes/login-callback-route.js +62 -0
  68. package/src/routes/logout-callback-route.js +72 -0
  69. package/src/state/.gitkeep +0 -0
  70. package/src/state/ReduxWrapper.js +29 -0
  71. package/src/state/storage.js +21 -0
  72. package/src/state/store.js +43 -0
  73. package/src/templates/.gitkeep +0 -0
  74. package/src/theme.js +36 -0
  75. package/src/utils/cacheUtils.js +48 -0
  76. package/src/utils/cssUtils.js +62 -0
  77. package/src/utils/envVariables.js +52 -0
  78. package/src/utils/expiredToken.js +15 -0
  79. package/src/utils/filePath.js +95 -0
  80. package/static/admin/admin.css +3 -0
  81. package/static/fonts/fonts.css +65 -0
  82. package/static/fonts/nunito-sans/nunito-sans-v12-latin-300.woff +0 -0
  83. package/static/fonts/nunito-sans/nunito-sans-v12-latin-300.woff2 +0 -0
  84. package/static/fonts/nunito-sans/nunito-sans-v12-latin-300italic.woff +0 -0
  85. package/static/fonts/nunito-sans/nunito-sans-v12-latin-300italic.woff2 +0 -0
  86. package/static/fonts/nunito-sans/nunito-sans-v12-latin-600.woff +0 -0
  87. package/static/fonts/nunito-sans/nunito-sans-v12-latin-600.woff2 +0 -0
  88. package/static/fonts/nunito-sans/nunito-sans-v12-latin-600italic.woff +0 -0
  89. package/static/fonts/nunito-sans/nunito-sans-v12-latin-600italic.woff2 +0 -0
  90. package/static/fonts/nunito-sans/nunito-sans-v12-latin-700.woff +0 -0
  91. package/static/fonts/nunito-sans/nunito-sans-v12-latin-700.woff2 +0 -0
  92. package/static/fonts/nunito-sans/nunito-sans-v12-latin-700italic.woff +0 -0
  93. package/static/fonts/nunito-sans/nunito-sans-v12-latin-700italic.woff2 +0 -0
@@ -0,0 +1,460 @@
1
+ import * as React from "react";
2
+ import PropTypes from "prop-types";
3
+ import { Script, navigate } from "gatsby";
4
+ import { connect } from "react-redux";
5
+ import { doLogin } from "openstack-uicore-foundation/lib/security/methods";
6
+ import { Container } from "@mui/material";
7
+ import SearchIcon from "@mui/icons-material/Search";
8
+ import "@fortawesome/fontawesome-free/css/all.css";
9
+ import AppBar from "@mui/material/AppBar";
10
+ import Box from "@mui/material/Box";
11
+ import Toolbar from "@mui/material/Toolbar";
12
+ import IconButton from "@mui/material/IconButton";
13
+ import MenuIcon from "@mui/icons-material/Menu";
14
+ import AddIcon from "@mui/icons-material/Add";
15
+ import RemoveIcon from "@mui/icons-material/Remove";
16
+ import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
17
+ import Button from "@mui/material/Button";
18
+
19
+ import navbarContent from "../../content/navbar/index.json";
20
+
21
+ import styles from "./index.module.scss";
22
+ import {
23
+ SEARCH_WIDGET_BASE_URL,
24
+ getEnvVariable,
25
+ } from "../../utils/envVariables";
26
+
27
+ function Navbar({ isLoggedUser, member }) {
28
+ const [navbarMenu, setNavbarMenu] = React.useState(null);
29
+ const [isMobileMenuOpen, setIsMobileMenuOpen] = React.useState(false);
30
+ const [isSearchBarOpen, setIsSearchBarOpen] = React.useState(false);
31
+
32
+ const handleSetActiveMenu = (option) => {
33
+ setNavbarMenu(navbarMenu === option ? null : option);
34
+ };
35
+
36
+ const handleMobileMenuToggle = () => {
37
+ setIsMobileMenuOpen(!isMobileMenuOpen);
38
+ setNavbarMenu(null);
39
+ };
40
+
41
+ const onClickLogout = () => {
42
+ navigate("/auth/logout", {
43
+ state: {
44
+ backUrl: window.location.pathname,
45
+ },
46
+ });
47
+ };
48
+
49
+ const handleCloseSearchBar = (event) => {
50
+ if (event?.target.matches(".ossw-search-bar-close")) {
51
+ setIsSearchBarOpen(false);
52
+ }
53
+ };
54
+
55
+ React.useEffect(() => {
56
+ document.addEventListener("click", handleCloseSearchBar);
57
+ return () => {
58
+ document.removeEventListener("click", handleCloseSearchBar);
59
+ };
60
+ }, []);
61
+
62
+ React.useEffect(() => {
63
+ if (isSearchBarOpen) {
64
+ const inputElement = document.getElementById("search-bar-input");
65
+ if (inputElement) {
66
+ inputElement.focus();
67
+ }
68
+ }
69
+ }, [isSearchBarOpen]);
70
+
71
+ const handleNavigation = (url) => {
72
+ navigate(url);
73
+ };
74
+
75
+ return (
76
+ <nav role="navigation">
77
+ <Script
78
+ src={`${getEnvVariable(SEARCH_WIDGET_BASE_URL)}/widget/embed.min.js`}
79
+ onLoad={() => console.log("Script loaded")}
80
+ />
81
+ <AppBar
82
+ position="static"
83
+ className={styles.navbar}
84
+ sx={{ backgroundColor: "#fff", boxShadow: "none" }}
85
+ >
86
+ <Container maxWidth="xl">
87
+ <Toolbar disableGutters sx={{ flexWrap: "wrap" }}>
88
+ <Box className={styles.navbarHeader}>
89
+ <div className={styles.brandWrapper}>
90
+ <a
91
+ className={styles.navbarBrand}
92
+ href="/"
93
+ alt="Openstack logo"
94
+ aria-label="Go to homepage"
95
+ />
96
+ </div>
97
+ <Box
98
+ className={styles.searchWrapper}
99
+ sx={{ display: { xs: "none", md: "flex" } }}
100
+ >
101
+ <div
102
+ role="button"
103
+ tabIndex={0}
104
+ onKeyDown={(event) => {
105
+ if (event.key === "Enter" || event.key === " ") setIsSearchBarOpen(true);
106
+ }}
107
+ onClick={() => setIsSearchBarOpen(true)}
108
+ className={styles.searchComponent}
109
+ >
110
+ <SearchIcon className={styles.searchIcon} />
111
+ <span className={styles.headerSearchText}>Search</span>
112
+ </div>
113
+ <Box
114
+ className={`openstack-search-bar ${styles.openstackSearchBar}`}
115
+ style={{
116
+ visibility: isSearchBarOpen ? "visible" : "hidden",
117
+ opacity: isSearchBarOpen ? "1" : "0",
118
+ }}
119
+ data-baseurl={getEnvVariable(SEARCH_WIDGET_BASE_URL)}
120
+ data-context="www-openstack"
121
+ />
122
+ </Box>
123
+ </Box>
124
+ {/* Mobile Menu */}
125
+ <Box
126
+ className={styles.mobileMenuWrapper}
127
+ sx={{ flexGrow: 1, display: { xs: "flex", md: "none" } }}
128
+ >
129
+ <IconButton
130
+ className={styles.toggleIcon}
131
+ size="large"
132
+ aria-label="account of current user"
133
+ aria-controls="menu-appbar"
134
+ aria-haspopup="true"
135
+ onClick={() => handleMobileMenuToggle()}
136
+ color="inherit"
137
+ >
138
+ <MenuIcon />
139
+ </IconButton>
140
+ </Box>
141
+ {/* Desktop Menu */}
142
+ <Box
143
+ sx={{
144
+ flexGrow: 1,
145
+ display: { xs: "none", md: isSearchBarOpen ? "none" : "flex" },
146
+ justifyContent: "flex-end",
147
+ }}
148
+ >
149
+ {navbarContent.items.map(
150
+ (item) => item.display && (
151
+ <Button
152
+ className={styles.navbarOption}
153
+ key={item.title}
154
+ sx={{ my: 2, display: "block" }}
155
+ disableRipple
156
+ onClick={() => handleNavigation(item.link)}
157
+ >
158
+ {item.items?.length > 0 ? (
159
+ <>
160
+ {item.title}
161
+ <ArrowDropDownIcon className={styles.arrowDownIcon} />
162
+ <Box className={styles.dropDownMenu}>
163
+ {item.items.map((subItem) => (!subItem.title && !subItem.link ? (
164
+ <div className={styles.divider} />
165
+ ) : (
166
+ <Button
167
+ className={styles.dropdownMenuOption}
168
+ key={subItem.title}
169
+ onClick={() => handleNavigation(subItem.link)}
170
+ sx={{ display: "block" }}
171
+ disableRipple
172
+ >
173
+ {subItem.title}
174
+ </Button>
175
+ )))}
176
+ </Box>
177
+ </>
178
+ ) : (
179
+ item.title
180
+ )}
181
+ </Button>
182
+ ),
183
+ )}
184
+ </Box>
185
+ {isLoggedUser ? (
186
+ <Box
187
+ maxWidth="15%"
188
+ sx={{
189
+ display: {
190
+ xs: "none",
191
+ md: isSearchBarOpen ? "none" : "flex",
192
+ },
193
+ justifyContent: "flex-end",
194
+ marginLeft: "auto",
195
+ }}
196
+ >
197
+ <Button
198
+ className={`${styles.navbarOption}`}
199
+ sx={{ my: 2, display: "block" }}
200
+ disableRipple
201
+ >
202
+ My Account
203
+ <ArrowDropDownIcon className={styles.arrowDownIcon} />
204
+ <Box className={styles.dropDownMenu}>
205
+ <Button
206
+ className={styles.dropdownMenuOption}
207
+ onClick={() => handleNavigation("/profile/")}
208
+ sx={{ display: "block" }}
209
+ disableRipple
210
+ >
211
+ Edit Profile
212
+ </Button>
213
+ <Button
214
+ className={styles.dropdownMenuOption}
215
+ onClick={() => handleNavigation(
216
+ `/community/members/profile/${
217
+ member.id
218
+ }/${`${member.first_name.toLowerCase()}-${member.last_name.toLowerCase()}`}`,
219
+ )}
220
+ sx={{ display: "block" }}
221
+ disableRipple
222
+ >
223
+ View Public Profile
224
+ </Button>
225
+ <div className={styles.divider} />
226
+ <Button
227
+ className={styles.dropdownMenuOption}
228
+ sx={{ display: "block" }}
229
+ onClick={onClickLogout}
230
+ disableRipple
231
+ >
232
+ Log out
233
+ </Button>
234
+ </Box>
235
+ </Button>
236
+ </Box>
237
+ ) : (
238
+ <Box
239
+ maxWidth="15%"
240
+ sx={{
241
+ display: {
242
+ xs: "none",
243
+ md: isSearchBarOpen ? "none" : "flex",
244
+ },
245
+ justifyContent: "flex-end",
246
+ marginLeft: "auto",
247
+ }}
248
+ >
249
+ <Button
250
+ className={`${styles.navbarOption} ${styles.join}`}
251
+ sx={{ my: 2, display: "block" }}
252
+ disableRipple
253
+ >
254
+ Join
255
+ <ArrowDropDownIcon className={styles.arrowDownIcon} />
256
+ <Box className={styles.dropDownMenu}>
257
+ <Button
258
+ className={styles.dropdownMenuOption}
259
+ onClick={() => handleNavigation("https://openinfra.dev/join")}
260
+ sx={{ display: "block" }}
261
+ disableRipple
262
+ >
263
+ Sign up for Foundation Membership
264
+ </Button>
265
+ <Button
266
+ className={styles.dropdownMenuOption}
267
+ onClick={() => handleNavigation("https://openinfra.dev/join")}
268
+ sx={{ display: "block" }}
269
+ disableRipple
270
+ >
271
+ Sponsor the Foundation
272
+ </Button>
273
+ <Button
274
+ className={styles.dropdownMenuOption}
275
+ onClick={() => handleNavigation("https://openinfra.dev")}
276
+ sx={{ display: "block" }}
277
+ disableRipple
278
+ >
279
+ More about the Foundation
280
+ </Button>
281
+ </Box>
282
+ </Button>
283
+ <Button
284
+ className={`${styles.navbarOption} ${styles.login}`}
285
+ onClick={() => doLogin("/")}
286
+ sx={{ my: 2, display: "block" }}
287
+ disableRipple
288
+ >
289
+ Log in
290
+ </Button>
291
+ </Box>
292
+ )}
293
+ </Toolbar>
294
+ <Box
295
+ className={`${styles.openstackSearchBarMobile} openstack-search-bar ossw-mobile`}
296
+ style={{
297
+ visibility: isMobileMenuOpen ? "visible" : "hidden",
298
+ opacity: isMobileMenuOpen ? "1" : "0",
299
+ height: isMobileMenuOpen ? "auto" : 0,
300
+ }}
301
+ data-baseurl={getEnvVariable(SEARCH_WIDGET_BASE_URL)}
302
+ data-context="www-openstack"
303
+ />
304
+ {isMobileMenuOpen && (
305
+ <Box
306
+ className={styles.navbarMobileMenu}
307
+ sx={{
308
+ display: { xs: "flex", md: "none" },
309
+ }}
310
+ >
311
+ {navbarContent.items.map(
312
+ (item) => item.display && (
313
+ <>
314
+ <Button
315
+ disableRipple
316
+ className={styles.mobileOption}
317
+ key={`${item.title}-mobile`}
318
+ onClick={() => handleSetActiveMenu(item.title)}
319
+ >
320
+ {item.title}
321
+ {item.items?.length > 0
322
+ && (item.title === navbarMenu ? (
323
+ <RemoveIcon aria-hidden="true" />
324
+ ) : (
325
+ <AddIcon aria-hidden="true" />
326
+ ))}
327
+ </Button>
328
+ {item.items?.length > 0 && item.title === navbarMenu && (
329
+ <Box className={styles.mobileDropdownMenu}>
330
+ {item.items.map((subItem) => (!subItem.title && !subItem.link ? (
331
+ <div className={styles.divider} />
332
+ ) : (
333
+ <Button
334
+ className={styles.mobileDropdownOption}
335
+ key={subItem.title}
336
+ onClick={() => handleNavigation(subItem.link)}
337
+ sx={{ display: "block" }}
338
+ disableRipple
339
+ >
340
+ {subItem.title}
341
+ </Button>
342
+ )))}
343
+ </Box>
344
+ )}
345
+ </>
346
+ ),
347
+ )}
348
+ {isLoggedUser ? (
349
+ <>
350
+ <Button
351
+ className={styles.mobileOption}
352
+ onClick={() => handleSetActiveMenu("My Account")}
353
+ disableRipple
354
+ >
355
+ My Account
356
+ {navbarMenu === "My Account" ? (
357
+ <RemoveIcon aria-hidden="true" />
358
+ ) : (
359
+ <AddIcon aria-hidden="true" />
360
+ )}
361
+ </Button>
362
+ {navbarMenu === "My Account" && (
363
+ <Box className={styles.mobileDropdownMenu}>
364
+ <Button
365
+ className={styles.mobileDropdownOption}
366
+ onClick={() => handleNavigation("/profile/")}
367
+ sx={{ display: "block" }}
368
+ disableRipple
369
+ >
370
+ Edit Profile
371
+ </Button>
372
+ <Button
373
+ className={styles.mobileDropdownOption}
374
+ onClick={() => handleNavigation(
375
+ `/community/members/profile/${
376
+ member.id
377
+ }/${`${member.first_name.toLowerCase()}-${member.last_name.toLowerCase()}`}`,
378
+ )}
379
+ sx={{ display: "block" }}
380
+ disableRipple
381
+ >
382
+ View Public Profile
383
+ </Button>
384
+ <div className={styles.divider} />
385
+ <Button
386
+ className={styles.mobileDropdownOption}
387
+ sx={{ display: "block" }}
388
+ onClick={onClickLogout}
389
+ disableRipple
390
+ >
391
+ Log out
392
+ </Button>
393
+ </Box>
394
+ )}
395
+ </>
396
+ ) : (
397
+ <Box>
398
+ <Button
399
+ className={`${styles.navbarOption} ${styles.join}`}
400
+ sx={{ my: 2, display: "block" }}
401
+ disableRipple
402
+ >
403
+ Join
404
+ <ArrowDropDownIcon className={styles.arrowDownIcon} />
405
+ <Box className={styles.dropDownMenu}>
406
+ <Button
407
+ className={styles.dropdownMenuOption}
408
+ onClick={() => handleNavigation("https://openinfra.dev/join")}
409
+ sx={{ display: "block" }}
410
+ disableRipple
411
+ >
412
+ Sign up for Foundation Membership
413
+ </Button>
414
+ <Button
415
+ className={styles.dropdownMenuOption}
416
+ onClick={() => handleNavigation("https://openinfra.dev/join")}
417
+ sx={{ display: "block" }}
418
+ disableRipple
419
+ >
420
+ Sponsor the Foundation
421
+ </Button>
422
+ <Button
423
+ className={styles.dropdownMenuOption}
424
+ onClick={() => handleNavigation("https://openinfra.dev")}
425
+ sx={{ display: "block" }}
426
+ disableRipple
427
+ >
428
+ More about the Foundation
429
+ </Button>
430
+ </Box>
431
+ </Button>
432
+ <Button
433
+ className={`${styles.navbarOption} ${styles.login}`}
434
+ onClick={() => doLogin("/")}
435
+ sx={{ my: 2, display: "block" }}
436
+ disableRipple
437
+ >
438
+ Log in
439
+ </Button>
440
+ </Box>
441
+ )}
442
+ </Box>
443
+ )}
444
+ </Container>
445
+ </AppBar>
446
+ </nav>
447
+ );
448
+ }
449
+
450
+ Navbar.propTypes = {
451
+ isLoggedUser: PropTypes.bool.isRequired,
452
+ member: PropTypes.object.isRequired,
453
+ };
454
+
455
+ const mapStateToProps = ({ loggedUserState }) => ({
456
+ isLoggedUser: loggedUserState.isLoggedUser,
457
+ member: loggedUserState.member,
458
+ });
459
+
460
+ export default connect(mapStateToProps, {})(Navbar);