gatsby-theme-q3 3.0.4 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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;
|