gatsby-theme-q3 3.0.4 → 3.2.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.
- package/CHANGELOG.md +71 -35
- package/__tests__/config.int.test.js +2 -13
- package/gatsby-browser.js +24 -0
- package/gatsby-config.js +0 -29
- package/lib/components/PageWrapper.js +1 -9
- package/lib/components/SearchEngine.js +70 -20
- package/lib/components/Wrapper.js +2 -7
- package/lib/components/__tests__/SearchEngine.test.js +41 -0
- package/lib/components/useSiteMetaData.js +13 -12
- package/package.json +6 -6
- package/src/components/PageWrapper.jsx +0 -8
- package/src/components/SearchEngine.jsx +86 -23
- package/src/components/Wrapper.jsx +2 -8
- package/src/components/__tests__/SearchEngine.test.jsx +58 -0
- package/src/components/useSiteMetaData.js +17 -16
- package/lib/components/LocaleBundles.js +0 -42
- package/lib/components/useLocale.js +0 -31
- package/src/components/LocaleBundles.jsx +0 -37
- package/src/components/useLocale.js +0 -20
package/CHANGELOG.md
CHANGED
@@ -3,50 +3,86 @@
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
**Note:** Version bump only for package gatsby-theme-q3
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
## [3.0.3](https://github.com/3merge/q/compare/v3.0.2...v3.0.3) (2022-01-29)
|
15
|
-
|
16
|
-
|
17
|
-
### Bug Fixes
|
18
|
-
|
19
|
-
* major release bugs ([#372](https://github.com/3merge/q/issues/372)) ([d3e0681](https://github.com/3merge/q/commit/d3e0681a8d9ce61558b3aeaabe94ce8bc326dfa7))
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
## [3.0.2](https://github.com/3merge/q/compare/v3.0.1...v3.0.2) (2022-01-28)
|
26
|
-
|
27
|
-
**Note:** Version bump only for package gatsby-theme-q3
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
## [3.0.1](https://github.com/3merge/q/compare/v3.0.0...v3.0.1) (2022-01-28)
|
34
|
-
|
35
|
-
**Note:** Version bump only for package gatsby-theme-q3
|
36
|
-
|
6
|
+
# [3.2.0](https://github.com/3merge/q/compare/v3.1.5...v3.2.0) (2022-02-09)
|
37
7
|
|
38
8
|
|
9
|
+
### Features
|
39
10
|
|
40
|
-
|
41
|
-
# [3.0.0](https://github.com/3merge/q/compare/v2.3.13...v3.0.0) (2022-01-28)
|
42
|
-
|
43
|
-
**Note:** Version bump only for package gatsby-theme-q3
|
11
|
+
* text locale editor ([#377](https://github.com/3merge/q/issues/377)) ([e827875](https://github.com/3merge/q/commit/e8278757be7505554376c1d1d78d9b8900fdf35e))
|
44
12
|
|
45
13
|
|
46
14
|
|
47
15
|
|
48
16
|
|
49
17
|
|
18
|
+
## [3.1.5](https://github.com/3merge/q/compare/v3.1.4...v3.1.5) (2022-02-01)
|
19
|
+
|
20
|
+
**Note:** Version bump only for package gatsby-theme-q3
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
## [3.1.4](https://github.com/3merge/q/compare/v3.1.3...v3.1.4) (2022-02-01)
|
27
|
+
|
28
|
+
**Note:** Version bump only for package gatsby-theme-q3
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
# [3.1.0](https://github.com/3merge/q/compare/v3.0.4...v3.1.0) (2022-01-31)
|
35
|
+
|
36
|
+
**Note:** Version bump only for package gatsby-theme-q3
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
## [3.0.4](https://github.com/3merge/q/compare/v3.0.3...v3.0.4) (2022-01-29)
|
43
|
+
|
44
|
+
**Note:** Version bump only for package gatsby-theme-q3
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
## [3.0.3](https://github.com/3merge/q/compare/v3.0.2...v3.0.3) (2022-01-29)
|
51
|
+
|
52
|
+
|
53
|
+
### Bug Fixes
|
54
|
+
|
55
|
+
* major release bugs ([#372](https://github.com/3merge/q/issues/372)) ([d3e0681](https://github.com/3merge/q/commit/d3e0681a8d9ce61558b3aeaabe94ce8bc326dfa7))
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
## [3.0.2](https://github.com/3merge/q/compare/v3.0.1...v3.0.2) (2022-01-28)
|
62
|
+
|
63
|
+
**Note:** Version bump only for package gatsby-theme-q3
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
## [3.0.1](https://github.com/3merge/q/compare/v3.0.0...v3.0.1) (2022-01-28)
|
70
|
+
|
71
|
+
**Note:** Version bump only for package gatsby-theme-q3
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
# [3.0.0](https://github.com/3merge/q/compare/v2.3.13...v3.0.0) (2022-01-28)
|
78
|
+
|
79
|
+
**Note:** Version bump only for package gatsby-theme-q3
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
|
50
86
|
## [2.3.11](https://github.com/3merge/q/compare/v2.3.10...v2.3.11) (2022-01-18)
|
51
87
|
|
52
88
|
**Note:** Version bump only for package gatsby-theme-q3
|
@@ -2,7 +2,6 @@ import { get } from 'lodash';
|
|
2
2
|
import config from '../gatsby-config';
|
3
3
|
|
4
4
|
const CANONICAL = 'gatsby-plugin-canonical-urls';
|
5
|
-
const MANIFEST = 'gatsby-plugin-manifest';
|
6
5
|
const ROBOTS = 'gatsby-plugin-robots-txt';
|
7
6
|
|
8
7
|
const ENV = {
|
@@ -29,18 +28,8 @@ const checkPlugins = (args = {}, plugin) => {
|
|
29
28
|
|
30
29
|
describe('gatsby-config', () => {
|
31
30
|
describe('plugins', () => {
|
32
|
-
it('should error without contentful access token', () => {
|
33
|
-
process.env.URL =
|
34
|
-
'https://development.netlify.3merge.com';
|
35
|
-
expect(() =>
|
36
|
-
config({
|
37
|
-
contentfulSpaceID: '1',
|
38
|
-
}),
|
39
|
-
).toThrowError();
|
40
|
-
});
|
41
|
-
|
42
31
|
it('should include conditional plugins', () =>
|
43
|
-
[CANONICAL
|
32
|
+
[CANONICAL].forEach((name) =>
|
44
33
|
checkPlugins(
|
45
34
|
{
|
46
35
|
brandingColor: '#FFF',
|
@@ -52,7 +41,7 @@ describe('gatsby-config', () => {
|
|
52
41
|
));
|
53
42
|
|
54
43
|
it('should exclude conditional plugins', () =>
|
55
|
-
[CANONICAL
|
44
|
+
[CANONICAL].forEach((name) =>
|
56
45
|
checkPlugins({}, name).hasNot(),
|
57
46
|
));
|
58
47
|
|
package/gatsby-browser.js
CHANGED
@@ -1,4 +1,28 @@
|
|
1
|
+
import axios from 'axios';
|
2
|
+
import { last, size } from 'lodash';
|
3
|
+
import { getDomain } from 'q3-admin';
|
4
|
+
import { browser } from 'q3-ui-helpers';
|
5
|
+
|
1
6
|
export {
|
2
7
|
wrapPageElement,
|
3
8
|
wrapRootElement,
|
4
9
|
} from './gatsby-ssr';
|
10
|
+
|
11
|
+
export const onClientEntry = async () => {
|
12
|
+
if (!browser.isBrowserReady()) return;
|
13
|
+
|
14
|
+
// set language default
|
15
|
+
axios.defaults.headers['Content-Language'] =
|
16
|
+
window.localStorage.getItem('q3-locale') || 'en';
|
17
|
+
|
18
|
+
// set tenant default
|
19
|
+
const { host } = window.location;
|
20
|
+
const parts = String(host).split('.').reverse();
|
21
|
+
|
22
|
+
if (size(parts) > 1)
|
23
|
+
axios.defaults.headers['X-Session-Tenant'] =
|
24
|
+
last(parts);
|
25
|
+
|
26
|
+
// calls Q3 API
|
27
|
+
await getDomain();
|
28
|
+
};
|
package/gatsby-config.js
CHANGED
@@ -5,8 +5,6 @@ const genKey = (url) =>
|
|
5
5
|
String(url).includes('netlify') ? 'disallow' : 'allow';
|
6
6
|
|
7
7
|
module.exports = ({
|
8
|
-
contentfulSpaceID,
|
9
|
-
contentfulAccessToken,
|
10
8
|
siteUrl,
|
11
9
|
title,
|
12
10
|
brandingColor,
|
@@ -52,19 +50,6 @@ module.exports = ({
|
|
52
50
|
},
|
53
51
|
];
|
54
52
|
|
55
|
-
if (contentfulSpaceID) {
|
56
|
-
if (!contentfulAccessToken)
|
57
|
-
throw new Error('Contentful access token missing');
|
58
|
-
|
59
|
-
plugins.push({
|
60
|
-
resolve: 'gatsby-source-contentful',
|
61
|
-
options: {
|
62
|
-
spaceId: contentfulSpaceID,
|
63
|
-
accessToken: contentfulAccessToken,
|
64
|
-
},
|
65
|
-
});
|
66
|
-
}
|
67
|
-
|
68
53
|
if (netlify)
|
69
54
|
plugins.push({
|
70
55
|
resolve: 'gatsby-plugin-netlify',
|
@@ -73,20 +58,6 @@ module.exports = ({
|
|
73
58
|
},
|
74
59
|
});
|
75
60
|
|
76
|
-
if (title && brandingColor)
|
77
|
-
plugins.push({
|
78
|
-
resolve: 'gatsby-plugin-manifest',
|
79
|
-
options: {
|
80
|
-
short_name: title,
|
81
|
-
start_url: '/',
|
82
|
-
background_color: '#FFF',
|
83
|
-
theme_color: brandingColor,
|
84
|
-
display: 'standalone',
|
85
|
-
name: title,
|
86
|
-
icon,
|
87
|
-
},
|
88
|
-
});
|
89
|
-
|
90
61
|
if (siteUrl)
|
91
62
|
plugins.push({
|
92
63
|
resolve: 'gatsby-plugin-canonical-urls',
|
@@ -13,21 +13,13 @@ var _components = require("q3-admin/lib/components");
|
|
13
13
|
|
14
14
|
var _SearchEngine = _interopRequireDefault(require("./SearchEngine"));
|
15
15
|
|
16
|
-
var _useLocale = _interopRequireDefault(require("./useLocale"));
|
17
|
-
|
18
16
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
19
17
|
|
20
|
-
// cannot conditionally call hooks otherwise
|
21
|
-
const Locale = () => {
|
22
|
-
(0, _useLocale.default)();
|
23
|
-
return null;
|
24
|
-
};
|
25
|
-
|
26
18
|
const PageWrapper = ({
|
27
19
|
children,
|
28
20
|
includeLoader,
|
29
21
|
includeLocale
|
30
|
-
}) => /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_SearchEngine.default, null), includeLoader && /*#__PURE__*/_react.default.createElement(_components.Loader, null),
|
22
|
+
}) => /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_SearchEngine.default, null), includeLoader && /*#__PURE__*/_react.default.createElement(_components.Loader, null), children);
|
31
23
|
|
32
24
|
PageWrapper.defaultProps = {
|
33
25
|
children: null,
|
@@ -3,18 +3,79 @@
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
4
4
|
value: true
|
5
5
|
});
|
6
|
-
exports.default = void 0;
|
6
|
+
exports.getStartUrl = exports.generateMetaTitleOptions = exports.generateMetaDescriptionOptions = exports.generateManifest = exports.generateIcons = exports.generateBrand = exports.default = exports.Manifest = void 0;
|
7
7
|
|
8
8
|
var _react = _interopRequireDefault(require("react"));
|
9
9
|
|
10
|
+
var _lodash = require("lodash");
|
11
|
+
|
10
12
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
11
13
|
|
12
14
|
var _reactHelmet = require("react-helmet");
|
13
15
|
|
16
|
+
var _q3UiHelpers = require("q3-ui-helpers");
|
17
|
+
|
14
18
|
var _useSiteMetaData = _interopRequireDefault(require("./useSiteMetaData"));
|
15
19
|
|
16
20
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
17
21
|
|
22
|
+
const withContent = output => content => content && (0, _lodash.isFunction)(output) ? output(content) : [];
|
23
|
+
|
24
|
+
const getStartUrl = () => _q3UiHelpers.browser.isBrowserReady() ? (0, _lodash.get)(window, 'location.host') : '';
|
25
|
+
|
26
|
+
exports.getStartUrl = getStartUrl;
|
27
|
+
const generateMetaDescriptionOptions = withContent(content => [{
|
28
|
+
name: 'description',
|
29
|
+
content
|
30
|
+
}, {
|
31
|
+
property: 'og:description',
|
32
|
+
content
|
33
|
+
}, {
|
34
|
+
name: 'twitter:description',
|
35
|
+
content
|
36
|
+
}]);
|
37
|
+
exports.generateMetaDescriptionOptions = generateMetaDescriptionOptions;
|
38
|
+
const generateMetaTitleOptions = withContent(content => [{
|
39
|
+
property: 'og:title',
|
40
|
+
content
|
41
|
+
}, {
|
42
|
+
name: 'twitter:title',
|
43
|
+
content
|
44
|
+
}]);
|
45
|
+
exports.generateMetaTitleOptions = generateMetaTitleOptions;
|
46
|
+
|
47
|
+
const generateBrand = xs => xs ? `%s | ${xs}` : undefined;
|
48
|
+
|
49
|
+
exports.generateBrand = generateBrand;
|
50
|
+
|
51
|
+
const generateIcons = (site = {}) => site !== null && site !== void 0 && site.favicon ? [{
|
52
|
+
src: site.favicon,
|
53
|
+
sizes: '512x512',
|
54
|
+
type: 'image/png'
|
55
|
+
}] : [];
|
56
|
+
|
57
|
+
exports.generateIcons = generateIcons;
|
58
|
+
|
59
|
+
const generateManifest = (site = {}) => ({
|
60
|
+
background_color: site.color,
|
61
|
+
description: site.description,
|
62
|
+
display: 'fullscreen',
|
63
|
+
icons: generateIcons(site),
|
64
|
+
name: site.title,
|
65
|
+
start_url: getStartUrl(),
|
66
|
+
short_name: site.brand,
|
67
|
+
theme_color: site.color
|
68
|
+
});
|
69
|
+
|
70
|
+
exports.generateManifest = generateManifest;
|
71
|
+
|
72
|
+
const Manifest = props => (0, _lodash.isObject)(props) ? /*#__PURE__*/_react.default.createElement("link", {
|
73
|
+
rel: "manifest",
|
74
|
+
href: `data:application/manifest+json,${encodeURIComponent(JSON.stringify(props))}`
|
75
|
+
}) : null;
|
76
|
+
|
77
|
+
exports.Manifest = Manifest;
|
78
|
+
|
18
79
|
const SEO = ({
|
19
80
|
description,
|
20
81
|
lang,
|
@@ -23,35 +84,24 @@ const SEO = ({
|
|
23
84
|
}) => {
|
24
85
|
const site = (0, _useSiteMetaData.default)();
|
25
86
|
const metaDescription = description || site.description;
|
87
|
+
const metaTitle = title || site.title;
|
26
88
|
return /*#__PURE__*/_react.default.createElement(_reactHelmet.Helmet, {
|
27
89
|
htmlAttributes: {
|
28
90
|
lang
|
29
91
|
},
|
30
|
-
title:
|
31
|
-
titleTemplate:
|
32
|
-
meta: [{
|
33
|
-
name: 'description',
|
34
|
-
content: metaDescription
|
35
|
-
}, {
|
36
|
-
property: 'og:title',
|
37
|
-
content: title
|
38
|
-
}, {
|
39
|
-
property: 'og:description',
|
40
|
-
content: metaDescription
|
41
|
-
}, {
|
92
|
+
title: metaTitle,
|
93
|
+
titleTemplate: generateBrand((0, _lodash.get)(site, 'brand', 'Q3')),
|
94
|
+
meta: [...generateMetaTitleOptions(metaTitle), ...generateMetaDescriptionOptions(metaDescription), {
|
42
95
|
property: 'og:type',
|
43
96
|
content: 'website'
|
44
97
|
}, {
|
45
98
|
name: 'twitter:card',
|
46
99
|
content: 'summary'
|
47
|
-
}, {
|
48
|
-
name: 'twitter:title',
|
49
|
-
content: title
|
50
|
-
}, {
|
51
|
-
name: 'twitter:description',
|
52
|
-
content: metaDescription
|
53
100
|
}].concat(meta)
|
54
|
-
})
|
101
|
+
}, /*#__PURE__*/_react.default.createElement(Manifest, generateManifest(site)), /*#__PURE__*/_react.default.createElement("link", {
|
102
|
+
rel: "icon",
|
103
|
+
href: site.favicon
|
104
|
+
}));
|
55
105
|
};
|
56
106
|
|
57
107
|
SEO.defaultProps = {
|
@@ -13,8 +13,6 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
13
13
|
|
14
14
|
var _q3UiPermissions = _interopRequireDefault(require("q3-ui-permissions"));
|
15
15
|
|
16
|
-
var _LocaleBundles = _interopRequireDefault(require("./LocaleBundles"));
|
17
|
-
|
18
16
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
19
17
|
|
20
18
|
/* eslint-disable import/no-extraneous-dependencies */
|
@@ -25,13 +23,10 @@ const setBaseUrlForRest = (baseURL = process.env.GATSBY_APP_BASE_URL || 'http://
|
|
25
23
|
|
26
24
|
const Wrapper = ({
|
27
25
|
baseURL,
|
28
|
-
children
|
29
|
-
locale
|
26
|
+
children
|
30
27
|
}) => {
|
31
28
|
setBaseUrlForRest(baseURL);
|
32
|
-
return /*#__PURE__*/_react.default.createElement(
|
33
|
-
locale: locale
|
34
|
-
}, /*#__PURE__*/_react.default.createElement(_q3UiPermissions.default, null, children));
|
29
|
+
return /*#__PURE__*/_react.default.createElement(_q3UiPermissions.default, null, children);
|
35
30
|
};
|
36
31
|
|
37
32
|
Wrapper.defaultProps = {
|
@@ -0,0 +1,41 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
var _SearchEngine = require("../SearchEngine");
|
4
|
+
|
5
|
+
jest.mock('q3-ui-locale', () => ({
|
6
|
+
browser: {
|
7
|
+
isBrowserReady: jest.fn()
|
8
|
+
}
|
9
|
+
}));
|
10
|
+
const host = 'https://google.ca';
|
11
|
+
beforeEach(() => {
|
12
|
+
Object.defineProperty(window, 'location', {
|
13
|
+
value: {
|
14
|
+
host
|
15
|
+
}
|
16
|
+
});
|
17
|
+
});
|
18
|
+
describe('SearchEngine', () => {
|
19
|
+
it('should not render descriptions without content', () => {
|
20
|
+
expect((0, _SearchEngine.generateMetaDescriptionOptions)().length).toBe(0);
|
21
|
+
});
|
22
|
+
it('should render descriptions with content', () => {
|
23
|
+
expect((0, _SearchEngine.generateMetaDescriptionOptions)('foo').length).toBeGreaterThanOrEqual(1);
|
24
|
+
});
|
25
|
+
it('should return host', () => {
|
26
|
+
expect((0, _SearchEngine.getStartUrl)()).toMatch(host);
|
27
|
+
});
|
28
|
+
it('should render favicon', () => {
|
29
|
+
expect((0, _SearchEngine.generateIcons)({
|
30
|
+
favicon: host
|
31
|
+
})).toHaveLength(1);
|
32
|
+
});
|
33
|
+
it('should not render favicon', () => {
|
34
|
+
expect((0, _SearchEngine.generateIcons)({
|
35
|
+
favicon: undefined
|
36
|
+
})).toHaveLength(0);
|
37
|
+
});
|
38
|
+
it('should include template literals', () => {
|
39
|
+
expect((0, _SearchEngine.generateBrand)('3merge')).toMatch('%s | 3merge');
|
40
|
+
});
|
41
|
+
});
|
@@ -9,19 +9,20 @@ var _lodash = require("lodash");
|
|
9
9
|
|
10
10
|
var _gatsby = require("gatsby");
|
11
11
|
|
12
|
-
var
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
12
|
+
var _useRunTime = _interopRequireDefault(require("gatsby-theme-q3-mui/src/components/useRunTime"));
|
13
|
+
|
14
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
15
|
+
|
16
|
+
var _default = () => (0, _lodash.merge)((0, _lodash.get)((0, _gatsby.useStaticQuery)((0, _gatsby.graphql)`
|
17
|
+
query {
|
18
|
+
site {
|
19
|
+
siteMetadata {
|
20
|
+
appDirectory
|
21
|
+
description
|
22
|
+
title
|
23
|
+
}
|
22
24
|
}
|
23
25
|
}
|
24
|
-
}
|
25
|
-
`), 'site.siteMetadata', {});
|
26
|
+
`), 'site.siteMetadata', {}), (0, _useRunTime.default)());
|
26
27
|
|
27
28
|
exports.default = _default;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "gatsby-theme-q3",
|
3
|
-
"version": "3.0
|
3
|
+
"version": "3.2.0",
|
4
4
|
"main": "index.js",
|
5
5
|
"license": "MIT",
|
6
6
|
"peerDependencies": {
|
@@ -23,24 +23,24 @@
|
|
23
23
|
"gatsby-image": "^3.11.0",
|
24
24
|
"gatsby-plugin-canonical-urls": "^4.2.0",
|
25
25
|
"gatsby-plugin-force-trailing-slashes": "^1.0.5",
|
26
|
-
"gatsby-plugin-manifest": "^4.2.0",
|
27
26
|
"gatsby-plugin-material-ui": "^3.0.1",
|
28
27
|
"gatsby-plugin-netlify": "^3.14.0",
|
29
28
|
"gatsby-plugin-robots-txt": "^1.6.14",
|
30
29
|
"gatsby-plugin-sharp": "^4.2.0",
|
31
30
|
"gatsby-plugin-sitemap": "^5.2.0",
|
32
|
-
"gatsby-
|
33
|
-
"gatsby-theme-q3-mui": "^3.0.4",
|
31
|
+
"gatsby-theme-q3-mui": "^3.2.0",
|
34
32
|
"gatsby-transformer-sharp": "^4.2.0",
|
35
33
|
"lodash": "^4.17.20",
|
36
34
|
"process": "^0.11.10",
|
37
35
|
"prop-types": "^15.7.2",
|
38
|
-
"q3-ui-
|
36
|
+
"q3-ui-helpers": "^3.2.0",
|
37
|
+
"q3-ui-locale": "^3.2.0",
|
39
38
|
"query-string": "^7.0.1",
|
39
|
+
"react-helmet": "^6.1.0",
|
40
40
|
"react-share": "^4.3.1",
|
41
41
|
"slugify": "^1.6.3",
|
42
42
|
"transform-loader": "^0.2.4",
|
43
43
|
"yarn": "^1.22.17"
|
44
44
|
},
|
45
|
-
"gitHead": "
|
45
|
+
"gitHead": "e7b26a194d8779024212ba64ca440222c28d82c7"
|
46
46
|
}
|
@@ -2,13 +2,6 @@ import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
3
3
|
import { Loader } from 'q3-admin/lib/components';
|
4
4
|
import SearchEngine from './SearchEngine';
|
5
|
-
import useLocale from './useLocale';
|
6
|
-
|
7
|
-
// cannot conditionally call hooks otherwise
|
8
|
-
const Locale = () => {
|
9
|
-
useLocale();
|
10
|
-
return null;
|
11
|
-
};
|
12
5
|
|
13
6
|
const PageWrapper = ({
|
14
7
|
children,
|
@@ -18,7 +11,6 @@ const PageWrapper = ({
|
|
18
11
|
<>
|
19
12
|
<SearchEngine />
|
20
13
|
{includeLoader && <Loader />}
|
21
|
-
{includeLocale && <Locale />}
|
22
14
|
{children}
|
23
15
|
</>
|
24
16
|
);
|
@@ -1,32 +1,100 @@
|
|
1
1
|
import React from 'react';
|
2
|
+
import { get, isFunction, isObject } from 'lodash';
|
2
3
|
import PropTypes from 'prop-types';
|
3
4
|
import { Helmet } from 'react-helmet';
|
5
|
+
import { browser } from 'q3-ui-helpers';
|
4
6
|
import useSiteMetaData from './useSiteMetaData';
|
5
7
|
|
8
|
+
const withContent = (output) => (content) =>
|
9
|
+
content && isFunction(output) ? output(content) : [];
|
10
|
+
|
11
|
+
export const getStartUrl = () =>
|
12
|
+
browser.isBrowserReady()
|
13
|
+
? get(window, 'location.host')
|
14
|
+
: '';
|
15
|
+
|
16
|
+
export const generateMetaDescriptionOptions = withContent(
|
17
|
+
(content) => [
|
18
|
+
{
|
19
|
+
name: 'description',
|
20
|
+
content,
|
21
|
+
},
|
22
|
+
{
|
23
|
+
property: 'og:description',
|
24
|
+
content,
|
25
|
+
},
|
26
|
+
{
|
27
|
+
name: 'twitter:description',
|
28
|
+
content,
|
29
|
+
},
|
30
|
+
],
|
31
|
+
);
|
32
|
+
|
33
|
+
export const generateMetaTitleOptions = withContent(
|
34
|
+
(content) => [
|
35
|
+
{
|
36
|
+
property: 'og:title',
|
37
|
+
content,
|
38
|
+
},
|
39
|
+
{
|
40
|
+
name: 'twitter:title',
|
41
|
+
content,
|
42
|
+
},
|
43
|
+
],
|
44
|
+
);
|
45
|
+
|
46
|
+
export const generateBrand = (xs) =>
|
47
|
+
xs ? `%s | ${xs}` : undefined;
|
48
|
+
|
49
|
+
export const generateIcons = (site = {}) =>
|
50
|
+
site?.favicon
|
51
|
+
? [
|
52
|
+
{
|
53
|
+
src: site.favicon,
|
54
|
+
sizes: '512x512',
|
55
|
+
type: 'image/png',
|
56
|
+
},
|
57
|
+
]
|
58
|
+
: [];
|
59
|
+
|
60
|
+
export const generateManifest = (site = {}) => ({
|
61
|
+
background_color: site.color,
|
62
|
+
description: site.description,
|
63
|
+
display: 'fullscreen',
|
64
|
+
icons: generateIcons(site),
|
65
|
+
name: site.title,
|
66
|
+
start_url: getStartUrl(),
|
67
|
+
short_name: site.brand,
|
68
|
+
theme_color: site.color,
|
69
|
+
});
|
70
|
+
|
71
|
+
export const Manifest = (props) =>
|
72
|
+
isObject(props) ? (
|
73
|
+
<link
|
74
|
+
rel="manifest"
|
75
|
+
href={`data:application/manifest+json,${encodeURIComponent(
|
76
|
+
JSON.stringify(props),
|
77
|
+
)}`}
|
78
|
+
/>
|
79
|
+
) : null;
|
80
|
+
|
6
81
|
const SEO = ({ description, lang, meta, title }) => {
|
7
82
|
const site = useSiteMetaData();
|
8
83
|
const metaDescription = description || site.description;
|
84
|
+
const metaTitle = title || site.title;
|
9
85
|
|
10
86
|
return (
|
11
87
|
<Helmet
|
12
88
|
htmlAttributes={{
|
13
89
|
lang,
|
14
90
|
}}
|
15
|
-
title={
|
16
|
-
titleTemplate={
|
91
|
+
title={metaTitle}
|
92
|
+
titleTemplate={generateBrand(
|
93
|
+
get(site, 'brand', 'Q3'),
|
94
|
+
)}
|
17
95
|
meta={[
|
18
|
-
|
19
|
-
|
20
|
-
content: metaDescription,
|
21
|
-
},
|
22
|
-
{
|
23
|
-
property: 'og:title',
|
24
|
-
content: title,
|
25
|
-
},
|
26
|
-
{
|
27
|
-
property: 'og:description',
|
28
|
-
content: metaDescription,
|
29
|
-
},
|
96
|
+
...generateMetaTitleOptions(metaTitle),
|
97
|
+
...generateMetaDescriptionOptions(metaDescription),
|
30
98
|
{
|
31
99
|
property: 'og:type',
|
32
100
|
content: 'website',
|
@@ -35,16 +103,11 @@ const SEO = ({ description, lang, meta, title }) => {
|
|
35
103
|
name: 'twitter:card',
|
36
104
|
content: 'summary',
|
37
105
|
},
|
38
|
-
{
|
39
|
-
name: 'twitter:title',
|
40
|
-
content: title,
|
41
|
-
},
|
42
|
-
{
|
43
|
-
name: 'twitter:description',
|
44
|
-
content: metaDescription,
|
45
|
-
},
|
46
106
|
].concat(meta)}
|
47
|
-
|
107
|
+
>
|
108
|
+
<Manifest {...generateManifest(site)} />
|
109
|
+
<link rel="icon" href={site.favicon} />
|
110
|
+
</Helmet>
|
48
111
|
);
|
49
112
|
};
|
50
113
|
|
@@ -3,7 +3,6 @@ import React from 'react';
|
|
3
3
|
import axios from 'axios';
|
4
4
|
import PropTypes from 'prop-types';
|
5
5
|
import AuthProvider from 'q3-ui-permissions';
|
6
|
-
import LocaleBundles from './LocaleBundles';
|
7
6
|
|
8
7
|
const setBaseUrlForRest = (
|
9
8
|
baseURL = process.env.GATSBY_APP_BASE_URL ||
|
@@ -13,14 +12,9 @@ const setBaseUrlForRest = (
|
|
13
12
|
return axios.defaults;
|
14
13
|
};
|
15
14
|
|
16
|
-
const Wrapper = ({ baseURL, children
|
15
|
+
const Wrapper = ({ baseURL, children }) => {
|
17
16
|
setBaseUrlForRest(baseURL);
|
18
|
-
|
19
|
-
return (
|
20
|
-
<LocaleBundles locale={locale}>
|
21
|
-
<AuthProvider>{children}</AuthProvider>
|
22
|
-
</LocaleBundles>
|
23
|
-
);
|
17
|
+
return <AuthProvider>{children}</AuthProvider>;
|
24
18
|
};
|
25
19
|
|
26
20
|
Wrapper.defaultProps = {
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import {
|
2
|
+
generateMetaDescriptionOptions,
|
3
|
+
getStartUrl,
|
4
|
+
generateIcons,
|
5
|
+
generateBrand,
|
6
|
+
} from '../SearchEngine';
|
7
|
+
|
8
|
+
jest.mock('q3-ui-locale', () => ({
|
9
|
+
browser: {
|
10
|
+
isBrowserReady: jest.fn(),
|
11
|
+
},
|
12
|
+
}));
|
13
|
+
|
14
|
+
const host = 'https://google.ca';
|
15
|
+
|
16
|
+
beforeEach(() => {
|
17
|
+
Object.defineProperty(window, 'location', {
|
18
|
+
value: {
|
19
|
+
host,
|
20
|
+
},
|
21
|
+
});
|
22
|
+
});
|
23
|
+
|
24
|
+
describe('SearchEngine', () => {
|
25
|
+
it('should not render descriptions without content', () => {
|
26
|
+
expect(generateMetaDescriptionOptions().length).toBe(0);
|
27
|
+
});
|
28
|
+
|
29
|
+
it('should render descriptions with content', () => {
|
30
|
+
expect(
|
31
|
+
generateMetaDescriptionOptions('foo').length,
|
32
|
+
).toBeGreaterThanOrEqual(1);
|
33
|
+
});
|
34
|
+
|
35
|
+
it('should return host', () => {
|
36
|
+
expect(getStartUrl()).toMatch(host);
|
37
|
+
});
|
38
|
+
|
39
|
+
it('should render favicon', () => {
|
40
|
+
expect(
|
41
|
+
generateIcons({
|
42
|
+
favicon: host,
|
43
|
+
}),
|
44
|
+
).toHaveLength(1);
|
45
|
+
});
|
46
|
+
|
47
|
+
it('should not render favicon', () => {
|
48
|
+
expect(
|
49
|
+
generateIcons({
|
50
|
+
favicon: undefined,
|
51
|
+
}),
|
52
|
+
).toHaveLength(0);
|
53
|
+
});
|
54
|
+
|
55
|
+
it('should include template literals', () => {
|
56
|
+
expect(generateBrand('3merge')).toMatch('%s | 3merge');
|
57
|
+
});
|
58
|
+
});
|
@@ -1,22 +1,23 @@
|
|
1
|
-
import { get } from 'lodash';
|
1
|
+
import { get, merge } from 'lodash';
|
2
2
|
import { useStaticQuery, graphql } from 'gatsby';
|
3
|
+
import useRunTime from 'gatsby-theme-q3-mui/src/components/useRunTime';
|
3
4
|
|
4
5
|
export default () =>
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
title
|
6
|
+
merge(
|
7
|
+
get(
|
8
|
+
useStaticQuery(graphql`
|
9
|
+
query {
|
10
|
+
site {
|
11
|
+
siteMetadata {
|
12
|
+
appDirectory
|
13
|
+
description
|
14
|
+
title
|
15
|
+
}
|
16
16
|
}
|
17
17
|
}
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
`),
|
19
|
+
'site.siteMetadata',
|
20
|
+
{},
|
21
|
+
),
|
22
|
+
useRunTime(),
|
22
23
|
);
|
@@ -1,42 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
4
|
-
value: true
|
5
|
-
});
|
6
|
-
exports.default = void 0;
|
7
|
-
|
8
|
-
var _q3UiLocale = require("q3-ui-locale");
|
9
|
-
|
10
|
-
var _propTypes = _interopRequireDefault(require("prop-types"));
|
11
|
-
|
12
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
13
|
-
|
14
|
-
const registeri18ResourceBundles = contentData => {
|
15
|
-
if (!contentData || !('en' in contentData)) return;
|
16
|
-
Object.entries(contentData).forEach(([key, bundle]) => {
|
17
|
-
Object.entries(bundle).forEach(([namespace, data]) => {
|
18
|
-
_q3UiLocale.i18n.addResourceBundle(key, namespace, data, true, true);
|
19
|
-
});
|
20
|
-
});
|
21
|
-
};
|
22
|
-
|
23
|
-
const LocaleBundles = ({
|
24
|
-
children,
|
25
|
-
locale
|
26
|
-
}) => {
|
27
|
-
registeri18ResourceBundles(locale);
|
28
|
-
return children;
|
29
|
-
};
|
30
|
-
|
31
|
-
LocaleBundles.defaultProps = {
|
32
|
-
children: null,
|
33
|
-
locale: {}
|
34
|
-
};
|
35
|
-
LocaleBundles.propTypes = {
|
36
|
-
// eslint-disable-next-line
|
37
|
-
children: _propTypes.default.any,
|
38
|
-
// eslint-disable-next-line
|
39
|
-
locale: _propTypes.default.object
|
40
|
-
};
|
41
|
-
var _default = LocaleBundles;
|
42
|
-
exports.default = _default;
|
@@ -1,31 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
4
|
-
value: true
|
5
|
-
});
|
6
|
-
exports.default = void 0;
|
7
|
-
|
8
|
-
var _react = _interopRequireDefault(require("react"));
|
9
|
-
|
10
|
-
var _q3UiLocale = require("q3-ui-locale");
|
11
|
-
|
12
|
-
var _q3UiPermissions = require("q3-ui-permissions");
|
13
|
-
|
14
|
-
var _q3UiRest = require("q3-ui-rest");
|
15
|
-
|
16
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
17
|
-
|
18
|
-
const useLocale = () => {
|
19
|
-
var _React$useContext, _React$useContext$sta;
|
20
|
-
|
21
|
-
const profile = (_React$useContext = _react.default.useContext(_q3UiPermissions.AuthContext)) === null || _React$useContext === void 0 ? void 0 : (_React$useContext$sta = _React$useContext.state) === null || _React$useContext$sta === void 0 ? void 0 : _React$useContext$sta.profile;
|
22
|
-
const lng = profile === null || profile === void 0 ? void 0 : profile.lang;
|
23
|
-
(0, _q3UiRest.useTimezoneInterceptor)(profile === null || profile === void 0 ? void 0 : profile.timezone);
|
24
|
-
|
25
|
-
_react.default.useEffect(() => {
|
26
|
-
if (lng && _q3UiLocale.i18n.resolvedLanguage) _q3UiLocale.i18n.changeLanguage(lng);
|
27
|
-
}, [lng]);
|
28
|
-
};
|
29
|
-
|
30
|
-
var _default = useLocale;
|
31
|
-
exports.default = _default;
|
@@ -1,37 +0,0 @@
|
|
1
|
-
import { i18n } from 'q3-ui-locale';
|
2
|
-
import PropTypes from 'prop-types';
|
3
|
-
|
4
|
-
const registeri18ResourceBundles = (contentData) => {
|
5
|
-
if (!contentData || !('en' in contentData)) return;
|
6
|
-
|
7
|
-
Object.entries(contentData).forEach(([key, bundle]) => {
|
8
|
-
Object.entries(bundle).forEach(([namespace, data]) => {
|
9
|
-
i18n.addResourceBundle(
|
10
|
-
key,
|
11
|
-
namespace,
|
12
|
-
data,
|
13
|
-
true,
|
14
|
-
true,
|
15
|
-
);
|
16
|
-
});
|
17
|
-
});
|
18
|
-
};
|
19
|
-
|
20
|
-
const LocaleBundles = ({ children, locale }) => {
|
21
|
-
registeri18ResourceBundles(locale);
|
22
|
-
return children;
|
23
|
-
};
|
24
|
-
|
25
|
-
LocaleBundles.defaultProps = {
|
26
|
-
children: null,
|
27
|
-
locale: {},
|
28
|
-
};
|
29
|
-
|
30
|
-
LocaleBundles.propTypes = {
|
31
|
-
// eslint-disable-next-line
|
32
|
-
children: PropTypes.any,
|
33
|
-
// eslint-disable-next-line
|
34
|
-
locale: PropTypes.object,
|
35
|
-
};
|
36
|
-
|
37
|
-
export default LocaleBundles;
|
@@ -1,20 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import { i18n } from 'q3-ui-locale';
|
3
|
-
import { AuthContext } from 'q3-ui-permissions';
|
4
|
-
import { useTimezoneInterceptor } from 'q3-ui-rest';
|
5
|
-
|
6
|
-
const useLocale = () => {
|
7
|
-
const profile =
|
8
|
-
React.useContext(AuthContext)?.state?.profile;
|
9
|
-
|
10
|
-
const lng = profile?.lang;
|
11
|
-
|
12
|
-
useTimezoneInterceptor(profile?.timezone);
|
13
|
-
|
14
|
-
React.useEffect(() => {
|
15
|
-
if (lng && i18n.resolvedLanguage)
|
16
|
-
i18n.changeLanguage(lng);
|
17
|
-
}, [lng]);
|
18
|
-
};
|
19
|
-
|
20
|
-
export default useLocale;
|