@financial-times/qanda-ui 0.0.1-beta.9 → 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.
- package/README.md +2 -2
- package/dist/cjs/components/Author/index.js +2 -2
- package/dist/cjs/components/Calendar/Apps.js +34 -16
- package/dist/cjs/components/Calendar/CalendarListItem.js +5 -1
- package/dist/cjs/components/Calendar/index.js +7 -2
- package/dist/cjs/components/CountdownTimer/index.js +5 -1
- package/dist/cjs/components/EmbedFormField/index.js +15 -6
- package/dist/cjs/components/ErrorMessage/humanReadableError.js +81 -0
- package/dist/cjs/components/ErrorMessage/index.js +60 -0
- package/dist/cjs/components/Expander/index.js +16 -3
- package/dist/cjs/components/ExpertDrawer/index.js +8 -3
- package/dist/cjs/components/FloatingActionBar/index.js +54 -15
- package/dist/cjs/components/MessageBox/index.js +7 -0
- package/dist/cjs/components/Overlay/index.js +19 -5
- package/dist/cjs/components/Qanda.js +1 -7
- package/dist/cjs/components/QandaBlock/index.js +7 -4
- package/dist/cjs/components/QandaContainer.js +18 -3
- package/dist/cjs/components/QandaProvider.js +31 -1
- package/dist/cjs/components/QuestionForm/index.js +83 -35
- package/dist/cjs/components/QuestionForm/messagesByUserType.js +7 -5
- package/dist/cjs/components/Stream/index.js +32 -5
- package/dist/cjs/components/Survey/index.js +8 -0
- package/dist/cjs/config/qanda.js +6 -0
- package/dist/cjs/index.js +33 -4
- package/dist/cjs/services/comments-api.js +72 -12
- package/dist/cjs/services/listeners-middleware.js +170 -0
- package/dist/cjs/store/index.js +2 -0
- package/dist/cjs/store/stream.js +20 -2
- package/dist/cjs/store/updateComments.js +9 -4
- package/dist/cjs/utils/auth.js +3 -1
- package/dist/cjs/utils/strings.js +16 -0
- package/dist/cjs/utils/tracking.js +34 -0
- package/dist/esm/components/Author/index.js +1 -1
- package/dist/esm/components/Calendar/Apps.js +34 -16
- package/dist/esm/components/Calendar/CalendarListItem.js +5 -1
- package/dist/esm/components/Calendar/index.js +7 -2
- package/dist/esm/components/CountdownTimer/index.js +6 -2
- package/dist/esm/components/EmbedFormField/index.js +14 -8
- package/dist/esm/components/ErrorMessage/humanReadableError.js +78 -0
- package/dist/esm/components/ErrorMessage/index.js +54 -0
- package/dist/esm/components/Expander/index.js +16 -3
- package/dist/esm/components/ExpertDrawer/index.js +8 -3
- package/dist/esm/components/FloatingActionBar/index.js +55 -16
- package/dist/esm/components/MessageBox/index.js +5 -0
- package/dist/esm/components/Overlay/index.js +19 -5
- package/dist/esm/components/Qanda.js +2 -8
- package/dist/esm/components/QandaBlock/index.js +7 -4
- package/dist/esm/components/QandaContainer.js +20 -5
- package/dist/esm/components/QandaProvider.js +30 -1
- package/dist/esm/components/QuestionForm/index.js +82 -34
- package/dist/esm/components/QuestionForm/messagesByUserType.js +7 -5
- package/dist/esm/components/Stream/index.js +32 -5
- package/dist/esm/components/Survey/index.js +6 -0
- package/dist/esm/config/qanda.js +3 -0
- package/dist/esm/index.js +33 -4
- package/dist/esm/services/comments-api.js +71 -11
- package/dist/esm/services/listeners-middleware.js +167 -0
- package/dist/esm/store/expertSlice.js +1 -1
- package/dist/esm/store/index.js +2 -0
- package/dist/esm/store/questionSlice.js +1 -1
- package/dist/esm/store/stream.js +18 -1
- package/dist/esm/store/updateComments.js +9 -4
- package/dist/esm/utils/auth.js +3 -1
- package/dist/esm/utils/strings.js +13 -0
- package/dist/esm/utils/tracking.js +29 -0
- package/dist/qanda.scss +125 -43
- package/dist/types/components/Author/index.d.ts +1 -1
- package/dist/types/components/Calendar/Apps.d.ts +6 -2
- package/dist/types/components/Calendar/CalendarList.d.ts +1 -1
- package/dist/types/components/Calendar/CalendarListItem.d.ts +1 -1
- package/dist/types/components/EmbedFormField/index.d.ts +3 -1
- package/dist/types/components/ErrorMessage/humanReadableError.d.ts +29 -0
- package/dist/types/components/ErrorMessage/index.d.ts +8 -0
- package/dist/types/components/FloatingActionBar/index.d.ts +3 -1
- package/dist/types/components/Loader/index.d.ts +1 -1
- package/dist/types/components/{Overlay/MessageBox.d.ts → MessageBox/index.d.ts} +3 -2
- package/dist/types/components/Overlay/index.d.ts +3 -3
- package/dist/types/components/Qanda.d.ts +1 -1
- package/dist/types/components/QandaBlock/index.d.ts +1 -1
- package/dist/types/components/QandaProvider.d.ts +9 -1
- package/dist/types/components/QuestionForm/messagesByUserType.d.ts +2 -2
- package/dist/types/components/Stream/index.d.ts +4 -1
- package/dist/types/components/Survey/index.d.ts +7 -0
- package/dist/types/config/qanda.d.ts +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/services/comments-api.d.ts +30 -598
- package/dist/types/services/error-middleware.d.ts +2 -3
- package/dist/types/services/listeners-middleware.d.ts +11 -0
- package/dist/types/store/expertSlice.d.ts +9 -10
- package/dist/types/store/index.d.ts +72 -183
- package/dist/types/store/questionSlice.d.ts +9 -8
- package/dist/types/store/stream.d.ts +14 -7
- package/dist/types/store/user.d.ts +3 -2
- package/dist/types/utils/strings.d.ts +1 -0
- package/dist/types/utils/tracking.d.ts +4 -0
- package/package.json +18 -18
- package/dist/cjs/components/Overlay/MessageBox.js +0 -7
- package/dist/cjs/components/UserInfo.js +0 -22
- package/dist/esm/components/Overlay/MessageBox.js +0 -5
- package/dist/esm/components/UserInfo.js +0 -20
- package/dist/types/components/UserInfo.d.ts +0 -2
|
@@ -4,15 +4,29 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const jsx_runtime_1 = require("preact/jsx-runtime");
|
|
7
|
+
const react_1 = require("react");
|
|
7
8
|
const o3_button_1 = require("@financial-times/o3-button");
|
|
8
9
|
const classnames_1 = __importDefault(require("classnames"));
|
|
9
10
|
function Overlay({ title, children, closeCallback, options = {}, }) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
// Trap keyboard focus within the overlay for accessibility
|
|
12
|
+
const trapFocus = (event) => {
|
|
13
|
+
if (event.key !== 'Tab')
|
|
14
|
+
return;
|
|
15
|
+
if (event.target === endFocusBtn.current) {
|
|
16
|
+
titleRef.current?.focus();
|
|
17
|
+
}
|
|
18
|
+
else if (event.target === titleRef.current && event.shiftKey) {
|
|
19
|
+
endFocusBtn.current?.focus();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const titleRef = (0, react_1.useRef)(null);
|
|
23
|
+
const endFocusBtn = (0, react_1.useRef)(null);
|
|
24
|
+
(0, react_1.useEffect)(() => {
|
|
25
|
+
titleRef.current?.focus();
|
|
26
|
+
}, []);
|
|
27
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "overlay", role: "dialog", "aria-modal": "true", "aria-labelledby": "overlay-title", children: [!options.allowInteraction && (0, jsx_runtime_1.jsx)("div", { className: "overlay__outer" }), (0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)([
|
|
14
28
|
'overlay__inner',
|
|
15
29
|
{ 'overlay__inner--shadow': options.shadow },
|
|
16
|
-
]), children: [(0, jsx_runtime_1.jsx)("div", { className: "overlay__header", children: (0, jsx_runtime_1.jsxs)("div", { className: "overlay__header-bar", children: [(0, jsx_runtime_1.jsx)("span", { className: "o3-type-body-highlight overlay__header-title", children: title }), (0, jsx_runtime_1.jsx)(o3_button_1.Button, { attributes: { className: 'overlay__header-close' }, iconOnly: true, icon: "cross", label: "Close", type: "ghost", theme: "mono", onClick: () => closeCallback() })] }) }), (0, jsx_runtime_1.jsx)("div", { className: "overlay__content", children: children })] })] }));
|
|
30
|
+
]), children: [(0, jsx_runtime_1.jsx)("div", { className: "overlay__header", children: (0, jsx_runtime_1.jsxs)("div", { className: "overlay__header-bar", children: [(0, jsx_runtime_1.jsx)("span", { className: "o3-type-body-highlight overlay__header-title", id: "overlay-title", tabIndex: 0, ref: titleRef, onKeyDown: trapFocus, children: title }), (0, jsx_runtime_1.jsx)(o3_button_1.Button, { attributes: { className: 'overlay__header-close' }, iconOnly: true, icon: "cross", label: "Close", type: "ghost", theme: "mono", onClick: () => closeCallback() })] }) }), (0, jsx_runtime_1.jsx)("div", { className: "overlay__content", children: children })] }), (0, jsx_runtime_1.jsx)("button", { className: "o3-visually-hidden", "aria-label": "Close", onClick: () => closeCallback(), ref: endFocusBtn, onKeyDown: trapFocus, type: "button", children: "Close" })] }));
|
|
17
31
|
}
|
|
18
32
|
exports.default = Overlay;
|
|
@@ -26,12 +26,6 @@ function Qanda({ storyId, useStaging, title, commentsAPIHost, commentsAPIVersion
|
|
|
26
26
|
console.error('Missing required props');
|
|
27
27
|
return null;
|
|
28
28
|
}
|
|
29
|
-
return ((0, jsx_runtime_1.jsx)(react_redux_1.Provider, { store: store_1.store, children: (0, jsx_runtime_1.jsx)(QandaProvider_1.
|
|
30
|
-
storyId,
|
|
31
|
-
useStaging,
|
|
32
|
-
commentsAPIUrl: cleanCommentsAPIUrl,
|
|
33
|
-
title,
|
|
34
|
-
maxQuestionLength,
|
|
35
|
-
}, children: (0, jsx_runtime_1.jsx)(QandaContainer_1.default, { embedFormFieldContainer: embedFormFieldContainer, countdownTimerContainer: countdownTimerContainer }) }) }));
|
|
29
|
+
return ((0, jsx_runtime_1.jsx)(react_redux_1.Provider, { store: store_1.store, children: (0, jsx_runtime_1.jsx)(QandaProvider_1.QandaProvider, { storyId: storyId, useStaging: useStaging, commentsAPIUrl: cleanCommentsAPIUrl, title: title, maxQuestionLength: maxQuestionLength, children: (0, jsx_runtime_1.jsx)(QandaContainer_1.default, { embedFormFieldContainer: embedFormFieldContainer, countdownTimerContainer: countdownTimerContainer }) }) }));
|
|
36
30
|
}
|
|
37
31
|
exports.default = Qanda;
|
|
@@ -4,11 +4,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const jsx_runtime_1 = require("preact/jsx-runtime");
|
|
7
|
-
const
|
|
7
|
+
const Byline_1 = __importDefault(require("@financial-times/cp-content-pipeline-ui/lib/components/Byline"));
|
|
8
|
+
const RichText_1 = __importDefault(require("@financial-times/cp-content-pipeline-ui/lib/components/RichText"));
|
|
8
9
|
require("../Expander/index");
|
|
9
10
|
const index_1 = __importDefault(require("../Expander/index"));
|
|
10
11
|
const Author_1 = __importDefault(require("../Author"));
|
|
11
12
|
const react_1 = require("react");
|
|
13
|
+
const tracking_1 = require("../../utils/tracking");
|
|
12
14
|
function renderTime(publishedDate) {
|
|
13
15
|
const date = new Date(publishedDate);
|
|
14
16
|
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
|
@@ -22,7 +24,7 @@ function interpolateColor(factor, colorStart = 'ccc1b7', colorEnd = '4d4845', al
|
|
|
22
24
|
const alpha = alphaStart + factor * (alphaEnd - alphaStart);
|
|
23
25
|
return `rgba(${rgb.join(', ')}, ${alpha})`;
|
|
24
26
|
}
|
|
25
|
-
function QandaBlock({ id, body, children, byline,
|
|
27
|
+
function QandaBlock({ id, body, children, byline, summary, showAnswerTime = false, }) {
|
|
26
28
|
(0, react_1.useEffect)(() => {
|
|
27
29
|
let currentBubble = null;
|
|
28
30
|
const factorRef = { current: 0 };
|
|
@@ -54,7 +56,8 @@ function QandaBlock({ id, body, children, byline, subtitle, showAnswerTime = fal
|
|
|
54
56
|
const scale = 1 + 0.04 * factorRef.current;
|
|
55
57
|
const shadow = interpolateColor(factorRef.current);
|
|
56
58
|
currentBubble.style.transform = `scale(${scale})`;
|
|
57
|
-
|
|
59
|
+
// NB: drop-shadow blur radius is not calculated the same way as box-shadow blur radius, halving it matches
|
|
60
|
+
currentBubble.style.filter = `drop-shadow(0px 2px 4px ${shadow})`;
|
|
58
61
|
if (factorRef.current < 0.001 && distanceRatio > 0.95) {
|
|
59
62
|
currentBubble.removeAttribute('style');
|
|
60
63
|
currentBubble = null;
|
|
@@ -71,6 +74,6 @@ function QandaBlock({ id, body, children, byline, subtitle, showAnswerTime = fal
|
|
|
71
74
|
observer.disconnect();
|
|
72
75
|
};
|
|
73
76
|
}, []);
|
|
74
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "qanda-block", children: [(0, jsx_runtime_1.jsxs)("div", { className: "qanda-block__comment qanda-block__question", id: id, "data-testid": `qanda-block__question-${id}`, "data-trackable":
|
|
77
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "qanda-block", children: [(0, jsx_runtime_1.jsxs)("div", { className: "qanda-block__comment qanda-block__question", id: id, "data-testid": `qanda-block__question-${id}`, "data-trackable": (0, tracking_1.trackingNS)('question'), children: [summary && ((0, jsx_runtime_1.jsx)("div", { className: "o3-type-headline-sm qanda-block__question-summary", children: summary })), (0, jsx_runtime_1.jsxs)("div", { className: "o3-type-body-highlight qanda-block__comment-byline", children: [(0, jsx_runtime_1.jsx)("span", { className: "o3-visually-hidden", children: "Question by " }), (0, jsx_runtime_1.jsx)(Byline_1.default, { structuredContent: byline, showEditedBy: false })] }), (0, jsx_runtime_1.jsx)("div", { className: "o3-type-body-content-base qanda-block__comment-text", children: (0, jsx_runtime_1.jsx)(index_1.default, { className: "qanda-block__text", expandLabel: "Expand question", collapseLabel: "Collapse question", id: id, children: (0, jsx_runtime_1.jsx)(RichText_1.default, { structuredContent: body.structured }) }) })] }, id), children?.map((child) => child.body && ((0, jsx_runtime_1.jsxs)("div", { className: "qanda-block__comment qanda-block__answer", "data-testid": `qanda-block__answer-${child.id}`, "data-trackable": (0, tracking_1.trackingNS)('answer'), children: [(0, jsx_runtime_1.jsxs)("div", { className: "qanda-block__answer-header", children: [(0, jsx_runtime_1.jsxs)("div", { className: "o3-type-body-highlight qanda-block__comment-byline", children: [(0, jsx_runtime_1.jsx)("span", { className: "o3-visually-hidden", children: "Reply from " }), child.author ? ((0, jsx_runtime_1.jsx)(Author_1.default, { byline: child.byline, headshot: child.author.headshot, prefLabel: child.author.prefLabel, role: child.author.role, streamPage: child.author.streamPage })) : ((0, jsx_runtime_1.jsx)(Byline_1.default, { structuredContent: child.byline, showEditedBy: false }))] }), showAnswerTime && ((0, jsx_runtime_1.jsxs)("span", { className: "o3-type-detail", children: [(0, jsx_runtime_1.jsx)("span", { className: "o3-visually-hidden", children: "Published at " }), renderTime(child.publishedDate)] }))] }), (0, jsx_runtime_1.jsx)("div", { className: "o3-type-body-content-base qanda-block__comment-text", children: (0, jsx_runtime_1.jsx)(index_1.default, { className: "qanda-block__text", expandLabel: "Expand answer", collapseLabel: "Collapse answer", id: id, children: (0, jsx_runtime_1.jsx)(RichText_1.default, { structuredContent: child.body.structured }) }) })] }, child.id)))] }));
|
|
75
78
|
}
|
|
76
79
|
exports.default = QandaBlock;
|
|
@@ -9,15 +9,17 @@ const react_redux_1 = require("react-redux");
|
|
|
9
9
|
const user_1 = require("../store/user");
|
|
10
10
|
const expertSlice_1 = require("../store/expertSlice");
|
|
11
11
|
const QuestionForm_1 = __importDefault(require("./QuestionForm"));
|
|
12
|
-
const
|
|
12
|
+
const Survey_1 = __importDefault(require("./Survey"));
|
|
13
13
|
const ExpertDrawer_1 = __importDefault(require("./ExpertDrawer"));
|
|
14
14
|
const Stream_1 = __importDefault(require("./Stream"));
|
|
15
15
|
const Calendar_1 = __importDefault(require("./Calendar"));
|
|
16
16
|
const CountdownTimer_1 = __importDefault(require("./CountdownTimer"));
|
|
17
17
|
const FloatingActionBar_1 = __importDefault(require("./FloatingActionBar"));
|
|
18
18
|
const EmbedFormField_1 = __importDefault(require("./EmbedFormField"));
|
|
19
|
+
const ErrorMessage_1 = __importDefault(require("./ErrorMessage"));
|
|
19
20
|
const QandaProvider_1 = require("./QandaProvider");
|
|
20
21
|
const comments_api_1 = require("../services/comments-api");
|
|
22
|
+
const qanda_1 = require("../config/qanda");
|
|
21
23
|
function QandaContainer({ embedFormFieldContainer, countdownTimerContainer, }) {
|
|
22
24
|
const { storyId, useStaging, commentsAPIUrl } = (0, react_1.useContext)(QandaProvider_1.QandaContext);
|
|
23
25
|
const dispatch = (0, react_redux_1.useDispatch)();
|
|
@@ -25,7 +27,20 @@ function QandaContainer({ embedFormFieldContainer, countdownTimerContainer, }) {
|
|
|
25
27
|
storyId,
|
|
26
28
|
useStaging,
|
|
27
29
|
commentsAPIUrl,
|
|
30
|
+
status: 'pre-live',
|
|
28
31
|
});
|
|
32
|
+
const [ariaMessage, setAriaMessage] = (0, react_1.useState)('');
|
|
33
|
+
(0, react_1.useEffect)(() => {
|
|
34
|
+
if (isLoading) {
|
|
35
|
+
setAriaMessage(`Loading ${qanda_1.PRODUCT_NAME}`);
|
|
36
|
+
}
|
|
37
|
+
else if (error) {
|
|
38
|
+
setAriaMessage(`Error loading ${qanda_1.PRODUCT_NAME}`);
|
|
39
|
+
}
|
|
40
|
+
else if (data) {
|
|
41
|
+
setAriaMessage(`${qanda_1.PRODUCT_NAME} stream has been updated`);
|
|
42
|
+
}
|
|
43
|
+
}, [isLoading, error, data]);
|
|
29
44
|
const updatedQA = (0, comments_api_1.useUpdatedQA)({
|
|
30
45
|
storyId,
|
|
31
46
|
useStaging,
|
|
@@ -33,7 +48,7 @@ function QandaContainer({ embedFormFieldContainer, countdownTimerContainer, }) {
|
|
|
33
48
|
});
|
|
34
49
|
const status = updatedQA?.status || data?.status || 'pre-live';
|
|
35
50
|
const profile = (0, react_redux_1.useSelector)((state) => state.user.profile);
|
|
36
|
-
const { data: roleData } = (0, comments_api_1.useGetUserRolesQuery)({
|
|
51
|
+
const { data: roleData, error: roleError } = (0, comments_api_1.useGetUserRolesQuery)({
|
|
37
52
|
storyId,
|
|
38
53
|
useStaging,
|
|
39
54
|
commentsAPIUrl,
|
|
@@ -53,6 +68,6 @@ function QandaContainer({ embedFormFieldContainer, countdownTimerContainer, }) {
|
|
|
53
68
|
dispatch(comments_api_1.nextCommentsApi.util.resetApiState());
|
|
54
69
|
};
|
|
55
70
|
}, [dispatch, storyId]);
|
|
56
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "qanda-container",
|
|
71
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "qanda-container", children: [error && ((0, jsx_runtime_1.jsx)("div", { style: { marginBottom: '16px' }, children: (0, jsx_runtime_1.jsx)(ErrorMessage_1.default, { error: error, storyId: storyId, operation: "stream-load" }) })), roleError && !error && ((0, jsx_runtime_1.jsx)("div", { style: { marginBottom: '16px' }, children: (0, jsx_runtime_1.jsx)(ErrorMessage_1.default, { error: roleError, storyId: storyId, operation: "profile-load" }) })), !isLoading && !error && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [status !== 'close' && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [showExpertView ? (0, jsx_runtime_1.jsx)(ExpertDrawer_1.default, {}) : (0, jsx_runtime_1.jsx)(QuestionForm_1.default, {}), (0, jsx_runtime_1.jsx)(CountdownTimer_1.default, { container: countdownTimerContainer })] })), status === 'pre-live' && (0, jsx_runtime_1.jsx)(Calendar_1.default, {}), (0, jsx_runtime_1.jsx)(Stream_1.default, { status: status }), (0, jsx_runtime_1.jsx)(Survey_1.default, { storyId: storyId, status: status }), (status === 'live' || (status === 'pre-live' && showExpertView)) && ((0, jsx_runtime_1.jsx)(FloatingActionBar_1.default, { isLive: status === 'live' })), status !== 'close' && !showExpertView && ((0, jsx_runtime_1.jsx)(EmbedFormField_1.default, { container: embedFormFieldContainer, status: status }))] })), (0, jsx_runtime_1.jsx)("div", { "aria-live": "polite", "aria-atomic": "true", className: "o3-visually-hidden", children: ariaMessage })] }));
|
|
57
72
|
}
|
|
58
73
|
exports.default = QandaContainer;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.QandaContext = void 0;
|
|
3
|
+
exports.QandaProvider = exports.QandaContext = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("preact/jsx-runtime");
|
|
4
5
|
const react_1 = require("react");
|
|
5
6
|
exports.QandaContext = (0, react_1.createContext)({
|
|
6
7
|
storyId: '',
|
|
@@ -9,3 +10,32 @@ exports.QandaContext = (0, react_1.createContext)({
|
|
|
9
10
|
title: '',
|
|
10
11
|
maxQuestionLength: 0,
|
|
11
12
|
});
|
|
13
|
+
const QandaProvider = ({ children, storyId, useStaging, commentsAPIUrl, title, maxQuestionLength = 0, }) => {
|
|
14
|
+
const dialogOpenerRef = (0, react_1.useRef)(null);
|
|
15
|
+
const setDialogOpener = (0, react_1.useCallback)((opener) => {
|
|
16
|
+
dialogOpenerRef.current = opener;
|
|
17
|
+
}, []);
|
|
18
|
+
const focusDialogOpener = (0, react_1.useCallback)(() => {
|
|
19
|
+
dialogOpenerRef.current?.focus();
|
|
20
|
+
dialogOpenerRef.current = null;
|
|
21
|
+
}, []);
|
|
22
|
+
const contextValues = (0, react_1.useMemo)(() => ({
|
|
23
|
+
storyId,
|
|
24
|
+
useStaging,
|
|
25
|
+
commentsAPIUrl,
|
|
26
|
+
title,
|
|
27
|
+
maxQuestionLength,
|
|
28
|
+
setDialogOpener,
|
|
29
|
+
focusDialogOpener,
|
|
30
|
+
}), [
|
|
31
|
+
storyId,
|
|
32
|
+
useStaging,
|
|
33
|
+
commentsAPIUrl,
|
|
34
|
+
title,
|
|
35
|
+
maxQuestionLength,
|
|
36
|
+
setDialogOpener,
|
|
37
|
+
focusDialogOpener,
|
|
38
|
+
]);
|
|
39
|
+
return ((0, jsx_runtime_1.jsx)(exports.QandaContext.Provider, { value: contextValues, children: children }));
|
|
40
|
+
};
|
|
41
|
+
exports.QandaProvider = QandaProvider;
|
|
@@ -7,6 +7,7 @@ exports.default = QuestionForm;
|
|
|
7
7
|
const jsx_runtime_1 = require("preact/jsx-runtime");
|
|
8
8
|
const react_redux_1 = require("react-redux");
|
|
9
9
|
const classnames_1 = __importDefault(require("classnames"));
|
|
10
|
+
const o_tracking_1 = __importDefault(require("@financial-times/o-tracking"));
|
|
10
11
|
const react_1 = require("react");
|
|
11
12
|
const questionSlice_1 = require("../../store/questionSlice");
|
|
12
13
|
const QandaProvider_1 = require("../QandaProvider");
|
|
@@ -14,22 +15,39 @@ const comments_api_1 = require("../../services/comments-api");
|
|
|
14
15
|
const Overlay_1 = __importDefault(require("../Overlay"));
|
|
15
16
|
const Loader_1 = __importDefault(require("../Loader"));
|
|
16
17
|
const o3_button_1 = require("@financial-times/o3-button");
|
|
17
|
-
const MessageBox_1 = __importDefault(require("../
|
|
18
|
+
const MessageBox_1 = __importDefault(require("../MessageBox"));
|
|
19
|
+
const ErrorMessage_1 = __importDefault(require("../ErrorMessage"));
|
|
18
20
|
const user_1 = require("../../store/user");
|
|
19
21
|
const validate_display_name_1 = __importDefault(require("../../utils/validate-display-name"));
|
|
20
22
|
const messagesByUserType_1 = require("./messagesByUserType");
|
|
23
|
+
const tracking_1 = require("../../utils/tracking");
|
|
21
24
|
function QuestionForm() {
|
|
22
25
|
const dispatch = (0, react_redux_1.useDispatch)();
|
|
23
|
-
const { storyId, useStaging, commentsAPIUrl, maxQuestionLength } = (0, react_1.useContext)(QandaProvider_1.QandaContext);
|
|
26
|
+
const { storyId, useStaging, commentsAPIUrl, maxQuestionLength, focusDialogOpener, } = (0, react_1.useContext)(QandaProvider_1.QandaContext);
|
|
24
27
|
const [postAnonymously, setPostAnonymously] = (0, react_1.useState)(false);
|
|
25
28
|
const [newDisplayName, setNewDisplayName] = (0, react_1.useState)(null);
|
|
26
29
|
const [submitEnabled, setSubmitEnabled] = (0, react_1.useState)(false);
|
|
27
30
|
const { isFormOpen, question, displayNameError } = (0, react_redux_1.useSelector)((state) => state.question);
|
|
28
31
|
const { profile, error: userDisplayNameError } = (0, react_redux_1.useSelector)((state) => state.user);
|
|
32
|
+
const MIN_QUESTION_LENGTH = 20;
|
|
33
|
+
const MAX_QUESTION_LENGTH = maxQuestionLength || 300;
|
|
29
34
|
// NB: reset will have the effect of setting things like error to null and isSuccess to empty
|
|
30
35
|
// you use this when you want to clear the state of the usePostQuestionMutation hook as if you had just started on the page
|
|
31
36
|
// as we allow users to see things like error messages and success messages, we do not clear those until we try to send another mutation, otherwise those things will clear too early
|
|
32
37
|
const [postQuestion, { isLoading, isSuccess, error, reset }] = (0, comments_api_1.usePostQuestionMutation)();
|
|
38
|
+
(0, react_1.useEffect)(() => {
|
|
39
|
+
if (isFormOpen) {
|
|
40
|
+
o_tracking_1.default.view.init({
|
|
41
|
+
selector: '.overlay__content-box',
|
|
42
|
+
getContextData: () => {
|
|
43
|
+
return {
|
|
44
|
+
...tracking_1.baseTrackingData,
|
|
45
|
+
trigger_action: 'open_question_form',
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}, [isFormOpen]);
|
|
33
51
|
(0, react_1.useEffect)(() => {
|
|
34
52
|
if (userDisplayNameError) {
|
|
35
53
|
dispatch((0, questionSlice_1.updateDisplayNameError)('Sorry, we are unable to update display names. Please try again.'));
|
|
@@ -37,7 +55,8 @@ function QuestionForm() {
|
|
|
37
55
|
}, [userDisplayNameError]);
|
|
38
56
|
(0, react_1.useEffect)(() => {
|
|
39
57
|
setSubmitEnabled(Boolean((profile?.token || (newDisplayName && newDisplayName.length > 0)) &&
|
|
40
|
-
question.length >=
|
|
58
|
+
question.length >= MIN_QUESTION_LENGTH &&
|
|
59
|
+
question.length <= MAX_QUESTION_LENGTH));
|
|
41
60
|
}, [profile?.token, newDisplayName, question]);
|
|
42
61
|
(0, react_1.useEffect)(() => {
|
|
43
62
|
// we want to clear the display name error unless the person has attempted
|
|
@@ -46,14 +65,6 @@ function QuestionForm() {
|
|
|
46
65
|
dispatch((0, questionSlice_1.updateDisplayNameError)(null));
|
|
47
66
|
}
|
|
48
67
|
}, [newDisplayName]);
|
|
49
|
-
// HANDLING EVENTS
|
|
50
|
-
const trackEvent = (trackingData) => {
|
|
51
|
-
const event = new CustomEvent('oTracking.event', {
|
|
52
|
-
detail: trackingData,
|
|
53
|
-
bubbles: true,
|
|
54
|
-
});
|
|
55
|
-
document.body.dispatchEvent(event);
|
|
56
|
-
};
|
|
57
68
|
const handleHideDisplayName = (e) => {
|
|
58
69
|
if (!profile?.token && !newDisplayName) {
|
|
59
70
|
dispatch((0, questionSlice_1.updateDisplayNameError)('You need a display name to ask a question, but you can still post it anonymously.'));
|
|
@@ -83,20 +94,25 @@ function QuestionForm() {
|
|
|
83
94
|
}
|
|
84
95
|
if (updatedProfile?.displayName) {
|
|
85
96
|
// NB: we are using a component act event as analytics team can only support existing categories and actions at this time
|
|
86
|
-
trackEvent({
|
|
87
|
-
|
|
88
|
-
|
|
97
|
+
(0, tracking_1.trackEvent)({
|
|
98
|
+
content: {
|
|
99
|
+
uuid: storyId,
|
|
100
|
+
},
|
|
89
101
|
trigger_action: 'success_set_displayname',
|
|
90
102
|
});
|
|
91
103
|
}
|
|
92
104
|
}
|
|
93
105
|
try {
|
|
106
|
+
// set nudge to false if the error is a suspect word, as the user can choose to submit it for moderator review
|
|
107
|
+
const errorCode = error?.data?.error?.errorCode ?? undefined;
|
|
108
|
+
const nudge = errorCode !== 'TOXIC_COMMENT';
|
|
94
109
|
await postQuestion({
|
|
95
110
|
token: updatedProfile?.token || profile?.token || '',
|
|
96
111
|
storyId,
|
|
97
112
|
useStaging,
|
|
98
113
|
commentsAPIUrl,
|
|
99
114
|
question: question,
|
|
115
|
+
nudge,
|
|
100
116
|
postAnonymously,
|
|
101
117
|
}).unwrap();
|
|
102
118
|
}
|
|
@@ -109,21 +125,6 @@ function QuestionForm() {
|
|
|
109
125
|
dispatch((0, questionSlice_1.updateQuestion)(''));
|
|
110
126
|
}
|
|
111
127
|
}, [isSuccess]);
|
|
112
|
-
if (error) {
|
|
113
|
-
// TODO: Remove tracking if confirmed that we can use AWS RUM
|
|
114
|
-
// Note: Events related to the Product, not reliability, should still use tracking
|
|
115
|
-
trackEvent({
|
|
116
|
-
category: 'component',
|
|
117
|
-
action: 'view',
|
|
118
|
-
trigger_action: 'liveqa_question_form_submit',
|
|
119
|
-
custom: [
|
|
120
|
-
{
|
|
121
|
-
name: 'state',
|
|
122
|
-
value: 'error',
|
|
123
|
-
},
|
|
124
|
-
],
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
128
|
if (!isFormOpen) {
|
|
128
129
|
return;
|
|
129
130
|
}
|
|
@@ -132,25 +133,72 @@ function QuestionForm() {
|
|
|
132
133
|
// are implemented by Origami team.
|
|
133
134
|
// Ticket CI-2770 was created to address this in the future.
|
|
134
135
|
(0, jsx_runtime_1.jsxs)(Overlay_1.default, { title: "Ask a question", closeCallback: () => {
|
|
136
|
+
focusDialogOpener?.();
|
|
135
137
|
setNewDisplayName(null);
|
|
136
138
|
dispatch((0, questionSlice_1.closeForm)());
|
|
137
|
-
|
|
139
|
+
reset();
|
|
140
|
+
}, children: [profile.isSubscribed && ((0, jsx_runtime_1.jsx)("div", { className: "overlay__content-box overlay__form-box", "data-testid": "question-form-overlay", "data-trackable-context-trigger_action": "open question form", children: (0, jsx_runtime_1.jsxs)("form", { "aria-busy": isLoading, onSubmit: handleSubmit, "data-trackable": (0, tracking_1.trackingNS)(`submit_question_form_${postAnonymously ? 'anonymous' : 'displayname'}`), children: [(0, jsx_runtime_1.jsxs)("div", { className: "overlay__user-container", children: [!postAnonymously && profile?.displayName && ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsxs)("span", { className: "overlay__user-container-display-name o3-type-body-highlight", children: [(0, jsx_runtime_1.jsx)("i", { className: "overlay__message-icon", style: { maskImage: 'var(--o3-icon-user)' } }), (0, jsx_runtime_1.jsxs)("span", { className: "o3-visually-hidden", children: ["Your user name is", ' '] }), profile.displayName] }) })), !profile?.displayName && ((0, jsx_runtime_1.jsxs)("label", { htmlFor: "display-name", className: "question-form__displayname-input o-forms-field", "aria-live": "polite", "aria-atomic": "true", children: [(0, jsx_runtime_1.jsx)("span", { className: "o-forms-title", children: (0, jsx_runtime_1.jsx)("span", { className: "o3-type-body-highlight", children: "Choose a display name" }) }), (0, jsx_runtime_1.jsxs)("span", { className: (0, classnames_1.default)([
|
|
138
141
|
'question-form__displayname__input',
|
|
139
142
|
'o-forms-input',
|
|
140
143
|
'o-forms-input--text',
|
|
141
144
|
{ 'o-forms-input--invalid': !!displayNameError },
|
|
142
|
-
]), children: [(0, jsx_runtime_1.jsx)("input", { className: "question-form__displayname__input__field o3-type-body-lg", id: "display-name", value: newDisplayName || '', onChange: (e) => setNewDisplayName(e.currentTarget.value), type: postAnonymously ? 'password' : 'text', "aria-invalid": displayNameError ? 'true' :
|
|
143
|
-
'question_form__displayname-error-message' }), displayNameError && ((0, jsx_runtime_1.jsx)("span", { className: "question-form__displayname__input__error o3-type-detail", id: "question_form__displayname-error-message", children: (0, jsx_runtime_1.jsx)("span", { children: displayNameError }) }))] })] }))] }), (0, jsx_runtime_1.jsx)("div", { className: "o-forms-field", children: (0, jsx_runtime_1.jsxs)("label", { className: "o-forms-input o-forms-input--checkbox", htmlFor: "post-anonymously", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", name: "post-anonymously", id: "post-anonymously", value: "true", onChange: handleHideDisplayName }), (0, jsx_runtime_1.jsx)("span", { className: "o-forms-input__label", children: "Post anonymously (optional)" })] }) }), (0, jsx_runtime_1.jsxs)("label", { for: "question-text",
|
|
145
|
+
]), children: [(0, jsx_runtime_1.jsx)("input", { className: "question-form__displayname__input__field o3-type-body-lg", id: "display-name", value: newDisplayName || '', onChange: (e) => setNewDisplayName(e.currentTarget.value), type: postAnonymously ? 'password' : 'text', "aria-describedby": "question-form-disclaimer", "aria-invalid": displayNameError ? 'true' : undefined, "aria-errormessage": displayNameError ??
|
|
146
|
+
'question_form__displayname-error-message' }), displayNameError && ((0, jsx_runtime_1.jsx)("span", { className: "question-form__displayname__input__error o3-type-detail", id: "question_form__displayname-error-message", children: (0, jsx_runtime_1.jsx)("span", { children: displayNameError }) }))] })] }))] }), (0, jsx_runtime_1.jsx)("div", { className: "o-forms-field", children: (0, jsx_runtime_1.jsxs)("label", { className: "o-forms-input o-forms-input--checkbox", htmlFor: "post-anonymously", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", name: "post-anonymously", id: "post-anonymously", value: "true", onChange: handleHideDisplayName, "data-trackable-context-action": `Post anonymously ${postAnonymously ? 'off' : 'on'}`, "data-trackable": (0, tracking_1.trackingNS)(`question-form__post_anonymously-${postAnonymously ? 'off' : 'on'}`) }), (0, jsx_runtime_1.jsx)("span", { className: "o-forms-input__label", children: "Post anonymously (optional)" })] }) }), (0, jsx_runtime_1.jsxs)("label", { for: "question-text", className: "o-forms-field question-form__question__input", children: [(0, jsx_runtime_1.jsx)("span", { className: "o-forms-title", children: (0, jsx_runtime_1.jsx)("span", { className: "o3-type-body-highlight", children: "Enter your question" }) }), (0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)([
|
|
147
|
+
'o-forms-input',
|
|
148
|
+
'o-forms-input--textarea',
|
|
149
|
+
'input-textarea__container',
|
|
150
|
+
{
|
|
151
|
+
'input-textarea__error': question.length > MAX_QUESTION_LENGTH,
|
|
152
|
+
},
|
|
153
|
+
]), children: [(0, jsx_runtime_1.jsx)("textarea", { id: "question-text", name: "question", className: "input-textarea", value: question, onChange: (e) => dispatch((0, questionSlice_1.updateQuestion)(e.currentTarget.value)), "aria-label": "Your question", "aria-describedby": "question-form-description", placeholder: "Type your question", "aria-invalid": error ? 'true' : undefined, "aria-errormessage": error ? 'question-form-error-message' : undefined }), (0, jsx_runtime_1.jsx)("span", { className: "o3-type-detail input-textarea__counter", "aria-label": `Your question is ${question.length} characters long, maximum allowed is ${MAX_QUESTION_LENGTH}`, children: `${question.length}/${MAX_QUESTION_LENGTH}` })] })] }), (0, jsx_runtime_1.jsx)("span", { id: "question-form-description", "aria-live": "polite", className: (0, classnames_1.default)([
|
|
154
|
+
'o3-type-detail',
|
|
155
|
+
{
|
|
156
|
+
'input--textarea__error-message': question.length > MAX_QUESTION_LENGTH,
|
|
157
|
+
},
|
|
158
|
+
]), children: question.length > MAX_QUESTION_LENGTH
|
|
159
|
+
? `Questions must not exceed ${MAX_QUESTION_LENGTH} characters.`
|
|
160
|
+
: `Questions must be at least ${MIN_QUESTION_LENGTH} characters long.` }), (0, jsx_runtime_1.jsx)(o3_button_1.Button, { attributes: {
|
|
144
161
|
className: 'overlay__submit-button',
|
|
145
162
|
type: 'submit',
|
|
146
163
|
disabled: !submitEnabled,
|
|
147
|
-
ariaLabel: question.length <
|
|
148
|
-
?
|
|
164
|
+
ariaLabel: question.length < MIN_QUESTION_LENGTH
|
|
165
|
+
? `Submit button (disabled until question is at least ${MIN_QUESTION_LENGTH} characters)`
|
|
149
166
|
: isLoading
|
|
150
167
|
? 'Submitting...'
|
|
151
168
|
: 'Submit your question',
|
|
169
|
+
'data-trackable': (0, tracking_1.trackingNS)('question-form__submit-button'),
|
|
152
170
|
},
|
|
153
171
|
// @ts-expect-error - o3 Button is currently accepting a JSX element as label
|
|
154
172
|
// but the type definition needs to be updated by Origami team
|
|
155
|
-
label: isLoading ? (0, jsx_runtime_1.jsx)(Loader_1.default, {}) : 'Submit your question', type: "primary", theme: "mono" }), !profile?.token && ((0, jsx_runtime_1.jsx)("span", { className: "o3-type-detail", children: "Submitting a question will create a community profile with FT." }))] }) })), isSuccess && ((0, jsx_runtime_1.jsx)(MessageBox_1.default, { color: "#00572C",
|
|
173
|
+
label: isLoading ? (0, jsx_runtime_1.jsx)(Loader_1.default, {}) : 'Submit your question', type: "primary", theme: "mono" }), !profile?.token && ((0, jsx_runtime_1.jsx)("span", { className: "o3-type-detail", id: "question-form-disclaimer", children: "Submitting a question will create a community profile with FT." }))] }) })), isSuccess && ((0, jsx_runtime_1.jsx)("div", { role: "alert", "aria-live": "polite", children: (0, jsx_runtime_1.jsx)(MessageBox_1.default, { color: "#00572C", extraClassNames: "o-message--alert o-message--success", title: "Question sent", text: "Your question is in the queue and will be reviewed by our editors." }) })), (0, jsx_runtime_1.jsx)("div", { "aria-live": "polite", role: "alert", id: "question-form-error-message", children: error && ((0, jsx_runtime_1.jsx)(ErrorMessage_1.default, { error: error, storyId: storyId, errorMapping: {
|
|
174
|
+
// Messages for specific error codes
|
|
175
|
+
REPEAT_POST: {
|
|
176
|
+
title: 'Question already submitted',
|
|
177
|
+
message: "You've already submitted this question. Try asking something different and submit again.",
|
|
178
|
+
},
|
|
179
|
+
REJECTED: {
|
|
180
|
+
title: 'Question rejected',
|
|
181
|
+
message: 'This question has been rejected for language that violates our guidelines.',
|
|
182
|
+
},
|
|
183
|
+
RATE_LIMIT_EXCEEDED: {
|
|
184
|
+
title: 'Too many questions',
|
|
185
|
+
message: 'You are submitting too many questions in a short amount of time. Please wait sometime before submitting another question.',
|
|
186
|
+
},
|
|
187
|
+
TOXIC_COMMENT: {
|
|
188
|
+
title: 'Question blocked',
|
|
189
|
+
message: 'Are you sure? The language in your question might violate our community guidelines. You can edit it, or if you submit, it will need moderator review. This could slow down a reply from our experts',
|
|
190
|
+
},
|
|
191
|
+
serverError: {
|
|
192
|
+
title: 'Server error',
|
|
193
|
+
message: 'There was a server error. Please try submitting your question again.',
|
|
194
|
+
},
|
|
195
|
+
connectionError: {
|
|
196
|
+
title: 'Connection error',
|
|
197
|
+
message: "You can't submit your question. Check your internet connection or browser settings and try again.",
|
|
198
|
+
},
|
|
199
|
+
timeoutError: {
|
|
200
|
+
title: 'Timeout error',
|
|
201
|
+
message: 'The request timed out. Please try submitting your question again.',
|
|
202
|
+
},
|
|
203
|
+
}, operation: "submit-question" })) }), (0, jsx_runtime_1.jsx)(MessageBox_1.default, { extraClassNames: "o-message--notice o-message--inform", title: (0, messagesByUserType_1.getTitleByUser)(profile), text: (0, messagesByUserType_1.getTextByUser)(profile) })] }));
|
|
156
204
|
}
|
|
@@ -4,13 +4,15 @@ exports.TERMS_AND_CONDITIONS_URL = exports.COMMUNITY_GUIDELINES_URL = exports.SU
|
|
|
4
4
|
exports.getTitleByUser = getTitleByUser;
|
|
5
5
|
exports.getTextByUser = getTextByUser;
|
|
6
6
|
const jsx_runtime_1 = require("preact/jsx-runtime");
|
|
7
|
+
const qanda_1 = require("../../config/qanda");
|
|
8
|
+
const tracking_1 = require("../../utils/tracking");
|
|
7
9
|
exports.SIGN_IN_URL = 'https://www.ft.com/login';
|
|
8
10
|
exports.SUBSCRIBE_URL = 'https://subs.ft.com/products';
|
|
9
11
|
exports.COMMUNITY_GUIDELINES_URL = 'https://help.ft.com/help/article-queries/commenting-on-an-article/commenting-guidelines';
|
|
10
12
|
exports.TERMS_AND_CONDITIONS_URL = 'https://help.ft.com/help/legal-privacy/terms-conditions';
|
|
11
13
|
function getTitleByUser(profile) {
|
|
12
14
|
if (profile.isSubscribed) {
|
|
13
|
-
return
|
|
15
|
+
return `${qanda_1.PRODUCT_NAME} guidelines`;
|
|
14
16
|
}
|
|
15
17
|
if (profile.isTrialist) {
|
|
16
18
|
return 'You are still on a trial period';
|
|
@@ -20,14 +22,14 @@ function getTitleByUser(profile) {
|
|
|
20
22
|
}
|
|
21
23
|
function getTextByUser(profile) {
|
|
22
24
|
if (profile.isSubscribed) {
|
|
23
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Keep your comments respectful and easy to understand for our global audience. By submitting, you agree to follow our", ' ', (0, jsx_runtime_1.jsx)("a", { className: "message-box__content-link", "data-trackable":
|
|
25
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Keep your comments respectful and easy to understand for our global audience. By submitting, you agree to follow our", ' ', (0, jsx_runtime_1.jsx)("a", { className: "message-box__content-link", "data-trackable": (0, tracking_1.trackingNS)('question-form__community-guidelines'), "data-trackable-context-action": "Community guidelines", href: exports.COMMUNITY_GUIDELINES_URL, target: "_parent", children: "community guidelines" }), ' ', "and", ' ', (0, jsx_runtime_1.jsx)("a", { className: "message-box__content-link", "data-trackable": (0, tracking_1.trackingNS)('question-form__terms-and-conditions'), "data-trackable-context-action": "Terms and conditions", href: exports.TERMS_AND_CONDITIONS_URL, target: "_parent", children: "terms" }), "."] }));
|
|
24
26
|
}
|
|
25
27
|
if (profile.isTrialist) {
|
|
26
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["View our full", ' ', (0, jsx_runtime_1.jsx)("a", { className: "message-box__content-link", "data-trackable":
|
|
28
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["View our full", ' ', (0, jsx_runtime_1.jsx)("a", { className: "message-box__content-link", "data-trackable": (0, tracking_1.trackingNS)('question-form__subscribe-trialist'), "data-trackable-context-action": "Subscribe", href: exports.SUBSCRIBE_URL, target: "_parent", children: "subscription packages" }), ' ', "to join the conversation."] }));
|
|
27
29
|
}
|
|
28
30
|
if (profile.isRegistered) {
|
|
29
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("a", { className: "message-box__content-link", "data-trackable":
|
|
31
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("a", { className: "message-box__content-link", "data-trackable": (0, tracking_1.trackingNS)('question-form__subscribe-registered'), "data-trackable-context-action": "Subscribe", href: exports.SUBSCRIBE_URL, target: "_parent", children: "Subscribe" }), ' ', "to join the conversation."] }));
|
|
30
32
|
}
|
|
31
33
|
// Anonymous users
|
|
32
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Please", ' ', (0, jsx_runtime_1.jsx)("a", { className: "message-box__content-link", "data-trackable":
|
|
34
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Please", ' ', (0, jsx_runtime_1.jsx)("a", { className: "message-box__content-link", "data-trackable": (0, tracking_1.trackingNS)('question-form__sign-in-anonymous'), "data-trackable-context-action": "Sign in", href: exports.SIGN_IN_URL, target: "_parent", children: "sign in" }), ' ', "or", ' ', (0, jsx_runtime_1.jsx)("a", { className: "message-box__content-link", "data-trackable": (0, tracking_1.trackingNS)('question-form__subscribe-anonymous'), "data-trackable-context-action": "Subscribe", href: exports.SUBSCRIBE_URL, target: "_parent", children: "subscribe" }), ' ', "to join the conversation."] }));
|
|
33
35
|
}
|
|
@@ -6,25 +6,52 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const jsx_runtime_1 = require("preact/jsx-runtime");
|
|
7
7
|
const react_1 = require("react");
|
|
8
8
|
const react_redux_1 = require("react-redux");
|
|
9
|
+
const o_tracking_1 = __importDefault(require("@financial-times/o-tracking"));
|
|
9
10
|
const index_1 = __importDefault(require("../QandaBlock/index"));
|
|
10
11
|
const QandaProvider_1 = require("../QandaProvider");
|
|
11
12
|
const Loader_1 = __importDefault(require("../Loader"));
|
|
12
13
|
const comments_api_1 = require("../../services/comments-api");
|
|
13
|
-
|
|
14
|
+
const ErrorMessage_1 = __importDefault(require("../ErrorMessage"));
|
|
15
|
+
const tracking_1 = require("../../utils/tracking");
|
|
16
|
+
function Stream({ status = 'pre-live' }) {
|
|
14
17
|
const latestAnsweredQuestionId = (0, react_redux_1.useSelector)((state) => state.stream.latestAnsweredQuestionId);
|
|
15
18
|
const { storyId, useStaging, commentsAPIUrl } = (0, react_1.useContext)(QandaProvider_1.QandaContext);
|
|
16
19
|
const { data, error, isLoading } = (0, comments_api_1.useGetQandAStreamQuery)({
|
|
17
20
|
storyId,
|
|
18
21
|
useStaging,
|
|
19
22
|
commentsAPIUrl,
|
|
20
|
-
|
|
23
|
+
status,
|
|
24
|
+
},
|
|
25
|
+
// In comments api, the stream endpoint uses different queries based on the Q&A status (live stream vs pre-live stream),
|
|
26
|
+
// If the status changes and Qanda receives an update,
|
|
27
|
+
// we need to force RTK to re-fetch the data and show the appropriate stream.
|
|
28
|
+
{ refetchOnMountOrArgChange: true });
|
|
21
29
|
(0, react_1.useEffect)(() => {
|
|
22
30
|
const element = document.getElementById(latestAnsweredQuestionId);
|
|
23
31
|
element?.scrollIntoView({ behavior: 'smooth' });
|
|
24
32
|
}, [latestAnsweredQuestionId]);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
(0, react_1.useEffect)(() => {
|
|
34
|
+
if (!data || !status)
|
|
35
|
+
return;
|
|
36
|
+
o_tracking_1.default.view.init({
|
|
37
|
+
intersectionObserverThreshold: 0.0001,
|
|
38
|
+
selector: '.qanda__stream',
|
|
39
|
+
getContextData: () => {
|
|
40
|
+
return {
|
|
41
|
+
...tracking_1.baseTrackingData,
|
|
42
|
+
custom: [
|
|
43
|
+
{
|
|
44
|
+
name: 'status',
|
|
45
|
+
value: status,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}, [data, status]);
|
|
52
|
+
return ((0, jsx_runtime_1.jsxs)("section", { className: "qanda__stream", "data-trackable-context-trigger_action": "view qanda container", children: [data && data.qandas.length > 0 && ((0, jsx_runtime_1.jsx)("h2", { className: "o3-type-title-md qanda__stream-title", id: "qanda__stream-title", children: data.status && data.status === 'pre-live'
|
|
53
|
+
? 'Need inspiration? A selection of reader questions so far'
|
|
54
|
+
: 'What our readers are asking' })), (0, jsx_runtime_1.jsxs)("div", { className: "qanda__blocks-container", "data-testid": "qanda__blocks-container", id: "qanda__blocks-container", "aria-busy": isLoading, children: [isLoading && ((0, jsx_runtime_1.jsx)(Loader_1.default, { theme: "dark", extraClassNames: "qanda__stream-loader" })), error && ((0, jsx_runtime_1.jsx)(ErrorMessage_1.default, { error: error, storyId: storyId, operation: "loading-stream" })), data &&
|
|
28
55
|
data.qandas &&
|
|
29
56
|
data.qandas.map((qandaBlock) => ((0, jsx_runtime_1.jsx)(index_1.default, { id: qandaBlock.id, body: qandaBlock.body, type: "comment", publishedDate: qandaBlock.publishedDate, author: qandaBlock.author, byline: qandaBlock.byline, showAnswerTime: data.status === 'live', children: qandaBlock.children }, qandaBlock.id)))] })] }));
|
|
30
57
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const jsx_runtime_1 = require("preact/jsx-runtime");
|
|
4
|
+
const tracking_1 = require("../../utils/tracking");
|
|
5
|
+
function Survey({ storyId, status }) {
|
|
6
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "o-message o-message--action o-message--inform o-message--survey", style: { marginBottom: 'var(--o3-spacing-l)' }, "data-o-message-close": "false", "data-o-component": "o-message", children: (0, jsx_runtime_1.jsx)("div", { className: "o-message__container", children: (0, jsx_runtime_1.jsxs)("div", { className: "o-message__content", children: [(0, jsx_runtime_1.jsx)("p", { className: "o-message__content-main", style: { fontSize: 'var(--o3-type-body-base-font-size)' }, children: "How was your experience?" }), (0, jsx_runtime_1.jsx)("div", { className: "o-message__actions", children: (0, jsx_runtime_1.jsx)("a", { href: `https://www.feedback.ft.com/c/a/6HTDVvrHSyaKLZ5281McAo?content_id=${storyId}&qa_status=${status}`, className: "o-message__actions__primary", target: "_blank", rel: "noopener noreferrer", "data-trackable": (0, tracking_1.trackingNS)('survey_link'), children: "Share your feedback" }) })] }) }) }));
|
|
7
|
+
}
|
|
8
|
+
exports.default = Survey;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PRODUCT_NAME = void 0;
|
|
4
|
+
// This file contains configuration settings for the Q&A (AKA Ask an Expert) features of the application.
|
|
5
|
+
// User facing product name
|
|
6
|
+
exports.PRODUCT_NAME = 'Ask an Expert';
|