@jetbrains/ring-ui 5.0.100 → 5.1.1
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/components/popup/position.d.ts +1 -1
- package/components/popup/position.js +33 -43
- package/components/select/select__popup.js +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/popup/position.d.ts +1 -1
- package/dist/popup/position.js +39 -61
- package/dist/select/select__popup.js +1 -1
- package/package.json +4 -4
- package/components/analytics-ng/analytics-ng.examples.js +0 -61
- package/components/analytics-ng/analytics-ng.js +0 -87
- package/components/analytics-ng/analytics-ng.test.js +0 -82
- package/dist/analytics-ng/analytics-ng.js +0 -89
|
@@ -23,7 +23,7 @@ export interface PositionAttrs {
|
|
|
23
23
|
autoCorrectTopOverflow: boolean;
|
|
24
24
|
}
|
|
25
25
|
export declare const positionPropKeys: readonly ["directions", "autoPositioning", "autoCorrectTopOverflow", "sidePadding", "top", "left", "offset", "maxHeight", "minWidth"];
|
|
26
|
-
export declare function maxHeightForDirection(direction: Directions, anchorNode: Element,
|
|
26
|
+
export declare function maxHeightForDirection(direction: Directions, anchorNode: Element, container?: Element): number;
|
|
27
27
|
export default function position(attrs: PositionAttrs): {
|
|
28
28
|
styles: PositionStyles;
|
|
29
29
|
direction: Directions | null;
|
|
@@ -46,7 +46,9 @@ function verticalOverflow(styles, scrollingCoordinates, attrs) {
|
|
|
46
46
|
const viewportMinX = scrollingCoordinates.top + attrs.sidePadding;
|
|
47
47
|
const viewportMaxX = scrollingCoordinates.top + containerHeight - attrs.sidePadding;
|
|
48
48
|
const topOverflow = Math.max(viewportMinX - styles.top, 0);
|
|
49
|
-
const popupHeight = attrs.
|
|
49
|
+
const popupHeight = attrs.maxHeight && typeof attrs.maxHeight === 'number'
|
|
50
|
+
? Math.min(attrs.popup.scrollHeight, attrs.maxHeight)
|
|
51
|
+
: attrs.popup.scrollHeight;
|
|
50
52
|
const verticalDiff = styles.top + popupHeight - viewportMaxX;
|
|
51
53
|
const bottomOverflow = Math.max(verticalDiff, 0);
|
|
52
54
|
return topOverflow + bottomOverflow;
|
|
@@ -76,37 +78,26 @@ const defaultcontainerRect = {
|
|
|
76
78
|
top: 0,
|
|
77
79
|
left: 0
|
|
78
80
|
};
|
|
79
|
-
function
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
export function maxHeightForDirection(direction, anchorNode, container = document.documentElement) {
|
|
82
|
+
const MIN_POPUP_SIZE = 8;
|
|
83
|
+
const domRect = anchorNode.getBoundingClientRect();
|
|
84
|
+
let topMaxHeight;
|
|
85
|
+
let bottomMaxHeight;
|
|
86
|
+
if (container === document.documentElement) {
|
|
87
|
+
topMaxHeight = Math.max(domRect.top, MIN_POPUP_SIZE);
|
|
88
|
+
bottomMaxHeight = Math.max(getWindowHeight() - domRect.top - domRect.height, MIN_POPUP_SIZE);
|
|
85
89
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
else {
|
|
91
|
+
const containerRect = container.getBoundingClientRect();
|
|
92
|
+
topMaxHeight = Math.max(domRect.top - containerRect.top, 0);
|
|
93
|
+
const containerHeight = Math.max(containerRect.height,
|
|
94
|
+
// XXX
|
|
95
|
+
// If container is the document element
|
|
96
|
+
// then we check client height too because we may have situation when
|
|
97
|
+
// "height" from "getBoundingClientRect" less than "clientHeight".
|
|
98
|
+
container === document.documentElement ? container.clientHeight : 0);
|
|
99
|
+
bottomMaxHeight = Math.max(containerHeight - (topMaxHeight + domRect.height), 0);
|
|
95
100
|
}
|
|
96
|
-
return styles;
|
|
97
|
-
}
|
|
98
|
-
export function maxHeightForDirection(direction, anchorNode, containerNode) {
|
|
99
|
-
const container = containerNode || document.documentElement;
|
|
100
|
-
const domRect = anchorNode.getBoundingClientRect();
|
|
101
|
-
const containerRect = container.getBoundingClientRect();
|
|
102
|
-
const topMaxHeight = Math.max(domRect.top - containerRect.top, 0);
|
|
103
|
-
const containerHeight = Math.max(containerRect.height,
|
|
104
|
-
// XXX
|
|
105
|
-
// If container is the document element
|
|
106
|
-
// then we check client height too because we may have situation when
|
|
107
|
-
// "height" from "getBoundingClientRect" less then "clientHeight".
|
|
108
|
-
container === document.documentElement ? container.clientHeight : 0);
|
|
109
|
-
const bottomMaxHeight = Math.max(containerHeight - (topMaxHeight + domRect.height), 0);
|
|
110
101
|
switch (direction) {
|
|
111
102
|
case Directions.TOP_LEFT:
|
|
112
103
|
case Directions.TOP_CENTER:
|
|
@@ -126,7 +117,8 @@ export function maxHeightForDirection(direction, anchorNode, containerNode) {
|
|
|
126
117
|
case Directions.LEFT_CENTER:
|
|
127
118
|
return (domRect.height / 2) + Math.min(bottomMaxHeight / 2, topMaxHeight / 2);
|
|
128
119
|
default:
|
|
129
|
-
|
|
120
|
+
const exhaustiveCheck = direction;
|
|
121
|
+
throw new Error(exhaustiveCheck);
|
|
130
122
|
}
|
|
131
123
|
}
|
|
132
124
|
export default function position(attrs) {
|
|
@@ -146,7 +138,7 @@ export default function position(attrs) {
|
|
|
146
138
|
const overflowAttrs = { ...attrs, popup };
|
|
147
139
|
const directionsMatrix = getPositionStyles(popup, anchorRect, anchorLeft, anchorTop, offset);
|
|
148
140
|
if (!autoPositioning || directions.length === 1) {
|
|
149
|
-
styles = directionsMatrix[directions[0]];
|
|
141
|
+
styles = { ...directionsMatrix[directions[0]] };
|
|
150
142
|
chosenDirection = directions[0];
|
|
151
143
|
}
|
|
152
144
|
else {
|
|
@@ -161,7 +153,7 @@ export default function position(attrs) {
|
|
|
161
153
|
horizontalOverflow(stylesB, scroll, overflowAttrs);
|
|
162
154
|
return overflowA - overflowB;
|
|
163
155
|
});
|
|
164
|
-
styles = sortedByIncreasingOverflow[0].styles;
|
|
156
|
+
styles = { ...sortedByIncreasingOverflow[0].styles };
|
|
165
157
|
chosenDirection = sortedByIncreasingOverflow[0].direction;
|
|
166
158
|
}
|
|
167
159
|
// because of the anchor negative margin top and left also may become negative
|
|
@@ -178,16 +170,14 @@ export default function position(attrs) {
|
|
|
178
170
|
else if (maxHeight) {
|
|
179
171
|
styles.maxHeight = maxHeight;
|
|
180
172
|
}
|
|
181
|
-
if (autoCorrectTopOverflow) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
styles
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
scroll
|
|
190
|
-
});
|
|
173
|
+
if (autoCorrectTopOverflow && chosenDirection && anchor) {
|
|
174
|
+
const maxForDirection = maxHeightForDirection(chosenDirection, anchor) - sidePadding;
|
|
175
|
+
if (!styles.maxHeight || styles.maxHeight > maxForDirection) {
|
|
176
|
+
styles.maxHeight = maxForDirection;
|
|
177
|
+
if (styles.top === 0) {
|
|
178
|
+
styles.top = sidePadding;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
191
181
|
}
|
|
192
182
|
if (minWidth === MinWidth.TARGET || minWidth === 'target') {
|
|
193
183
|
styles.minWidth = anchorRect.width;
|
|
@@ -278,7 +278,7 @@ export default class SelectPopup extends PureComponent {
|
|
|
278
278
|
const anchorNode = this.props.anchorElement;
|
|
279
279
|
const containerNode = getPopupContainer(ringPopupTarget) || document.documentElement;
|
|
280
280
|
return anchorNode != null
|
|
281
|
-
? Math.min(directions.reduce((maxHeight, direction) => (Math.max(maxHeight, maxHeightForDirection(direction, anchorNode, getStyles(containerNode).position !== 'static' ? containerNode :
|
|
281
|
+
? Math.min(directions.reduce((maxHeight, direction) => (Math.max(maxHeight, maxHeightForDirection(direction, anchorNode, getStyles(containerNode).position !== 'static' ? containerNode : undefined) ?? 0)), minMaxHeight), userDefinedMaxHeight)
|
|
282
282
|
: userDefinedMaxHeight;
|
|
283
283
|
});
|
|
284
284
|
popupRef = (el) => {
|
package/dist/popup/position.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface PositionAttrs {
|
|
|
23
23
|
autoCorrectTopOverflow: boolean;
|
|
24
24
|
}
|
|
25
25
|
export declare const positionPropKeys: readonly ["directions", "autoPositioning", "autoCorrectTopOverflow", "sidePadding", "top", "left", "offset", "maxHeight", "minWidth"];
|
|
26
|
-
export declare function maxHeightForDirection(direction: Directions, anchorNode: Element,
|
|
26
|
+
export declare function maxHeightForDirection(direction: Directions, anchorNode: Element, container?: Element): number;
|
|
27
27
|
export default function position(attrs: PositionAttrs): {
|
|
28
28
|
styles: PositionStyles;
|
|
29
29
|
direction: Directions | null;
|
package/dist/popup/position.js
CHANGED
|
@@ -81,7 +81,7 @@ function verticalOverflow(styles, scrollingCoordinates, attrs) {
|
|
|
81
81
|
const viewportMinX = scrollingCoordinates.top + attrs.sidePadding;
|
|
82
82
|
const viewportMaxX = scrollingCoordinates.top + containerHeight - attrs.sidePadding;
|
|
83
83
|
const topOverflow = Math.max(viewportMinX - styles.top, 0);
|
|
84
|
-
const popupHeight = attrs.popup.
|
|
84
|
+
const popupHeight = attrs.maxHeight && typeof attrs.maxHeight === 'number' ? Math.min(attrs.popup.scrollHeight, attrs.maxHeight) : attrs.popup.scrollHeight;
|
|
85
85
|
const verticalDiff = styles.top + popupHeight - viewportMaxX;
|
|
86
86
|
const bottomOverflow = Math.max(verticalDiff, 0);
|
|
87
87
|
return topOverflow + bottomOverflow;
|
|
@@ -101,50 +101,26 @@ const defaultcontainerRect = {
|
|
|
101
101
|
top: 0,
|
|
102
102
|
left: 0
|
|
103
103
|
};
|
|
104
|
-
function
|
|
105
|
-
let
|
|
106
|
-
|
|
107
|
-
styles,
|
|
108
|
-
anchorRect,
|
|
109
|
-
maxHeight,
|
|
110
|
-
popupScrollHeight,
|
|
111
|
-
direction,
|
|
112
|
-
scroll
|
|
113
|
-
} = _ref;
|
|
114
|
-
const BORDER_COMPENSATION = 1;
|
|
115
|
-
const {
|
|
116
|
-
TOP_LEFT,
|
|
117
|
-
TOP_RIGHT,
|
|
118
|
-
TOP_CENTER,
|
|
119
|
-
RIGHT_TOP,
|
|
120
|
-
LEFT_TOP
|
|
121
|
-
} = Directions;
|
|
122
|
-
const openedToTop = direction != null && [TOP_LEFT, TOP_RIGHT, TOP_CENTER, RIGHT_TOP, LEFT_TOP].includes(direction);
|
|
123
|
-
if (!openedToTop) {
|
|
124
|
-
return styles;
|
|
125
|
-
}
|
|
126
|
-
const isAttachedToAnchorTop = direction != null && [TOP_LEFT, TOP_CENTER, TOP_RIGHT].includes(direction);
|
|
127
|
-
const attachingPointY = isAttachedToAnchorTop ? anchorRect.top : anchorRect.bottom;
|
|
128
|
-
const effectiveHeight = maxHeight && typeof maxHeight === 'number' ? Math.min(popupScrollHeight, maxHeight) : popupScrollHeight;
|
|
129
|
-
const hypotheticalTop = attachingPointY - effectiveHeight;
|
|
130
|
-
if (hypotheticalTop <= sidePadding) {
|
|
131
|
-
styles.top = sidePadding + scroll.top;
|
|
132
|
-
styles.maxHeight = attachingPointY - sidePadding + BORDER_COMPENSATION;
|
|
133
|
-
}
|
|
134
|
-
return styles;
|
|
135
|
-
}
|
|
136
|
-
function maxHeightForDirection(direction, anchorNode, containerNode) {
|
|
137
|
-
const container = containerNode || document.documentElement;
|
|
104
|
+
function maxHeightForDirection(direction, anchorNode) {
|
|
105
|
+
let container = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : document.documentElement;
|
|
106
|
+
const MIN_POPUP_SIZE = 8;
|
|
138
107
|
const domRect = anchorNode.getBoundingClientRect();
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
108
|
+
let topMaxHeight;
|
|
109
|
+
let bottomMaxHeight;
|
|
110
|
+
if (container === document.documentElement) {
|
|
111
|
+
topMaxHeight = Math.max(domRect.top, MIN_POPUP_SIZE);
|
|
112
|
+
bottomMaxHeight = Math.max(getWindowHeight() - domRect.top - domRect.height, MIN_POPUP_SIZE);
|
|
113
|
+
} else {
|
|
114
|
+
const containerRect = container.getBoundingClientRect();
|
|
115
|
+
topMaxHeight = Math.max(domRect.top - containerRect.top, 0);
|
|
116
|
+
const containerHeight = Math.max(containerRect.height,
|
|
117
|
+
// XXX
|
|
118
|
+
// If container is the document element
|
|
119
|
+
// then we check client height too because we may have situation when
|
|
120
|
+
// "height" from "getBoundingClientRect" less than "clientHeight".
|
|
121
|
+
container === document.documentElement ? container.clientHeight : 0);
|
|
122
|
+
bottomMaxHeight = Math.max(containerHeight - (topMaxHeight + domRect.height), 0);
|
|
123
|
+
}
|
|
148
124
|
switch (direction) {
|
|
149
125
|
case Directions.TOP_LEFT:
|
|
150
126
|
case Directions.TOP_CENTER:
|
|
@@ -164,7 +140,8 @@ function maxHeightForDirection(direction, anchorNode, containerNode) {
|
|
|
164
140
|
case Directions.LEFT_CENTER:
|
|
165
141
|
return domRect.height / 2 + Math.min(bottomMaxHeight / 2, topMaxHeight / 2);
|
|
166
142
|
default:
|
|
167
|
-
|
|
143
|
+
const exhaustiveCheck = direction;
|
|
144
|
+
throw new Error(exhaustiveCheck);
|
|
168
145
|
}
|
|
169
146
|
}
|
|
170
147
|
function position(attrs) {
|
|
@@ -200,7 +177,9 @@ function position(attrs) {
|
|
|
200
177
|
};
|
|
201
178
|
const directionsMatrix = getPositionStyles(popup, anchorRect, anchorLeft, anchorTop, offset);
|
|
202
179
|
if (!autoPositioning || directions.length === 1) {
|
|
203
|
-
styles =
|
|
180
|
+
styles = {
|
|
181
|
+
...directionsMatrix[directions[0]]
|
|
182
|
+
};
|
|
204
183
|
chosenDirection = directions[0];
|
|
205
184
|
} else {
|
|
206
185
|
const sortedByIncreasingOverflow = directions.
|
|
@@ -208,18 +187,20 @@ function position(attrs) {
|
|
|
208
187
|
concat(directions[0]).filter(direction => directionsMatrix[direction]).map(direction => ({
|
|
209
188
|
styles: directionsMatrix[direction],
|
|
210
189
|
direction
|
|
211
|
-
})).sort((
|
|
190
|
+
})).sort((_ref, _ref2) => {
|
|
212
191
|
let {
|
|
213
192
|
styles: stylesA
|
|
214
|
-
} =
|
|
193
|
+
} = _ref;
|
|
215
194
|
let {
|
|
216
195
|
styles: stylesB
|
|
217
|
-
} =
|
|
196
|
+
} = _ref2;
|
|
218
197
|
const overflowA = verticalOverflow(stylesA, scroll, overflowAttrs) + horizontalOverflow(stylesA, scroll, overflowAttrs);
|
|
219
198
|
const overflowB = verticalOverflow(stylesB, scroll, overflowAttrs) + horizontalOverflow(stylesB, scroll, overflowAttrs);
|
|
220
199
|
return overflowA - overflowB;
|
|
221
200
|
});
|
|
222
|
-
styles =
|
|
201
|
+
styles = {
|
|
202
|
+
...sortedByIncreasingOverflow[0].styles
|
|
203
|
+
};
|
|
223
204
|
chosenDirection = sortedByIncreasingOverflow[0].direction;
|
|
224
205
|
}
|
|
225
206
|
// because of the anchor negative margin top and left also may become negative
|
|
@@ -235,17 +216,14 @@ function position(attrs) {
|
|
|
235
216
|
} else if (maxHeight) {
|
|
236
217
|
styles.maxHeight = maxHeight;
|
|
237
218
|
}
|
|
238
|
-
if (autoCorrectTopOverflow) {
|
|
239
|
-
|
|
240
|
-
styles
|
|
241
|
-
|
|
242
|
-
styles
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
popupScrollHeight: (_popup$scrollHeight = popup?.scrollHeight) !== null && _popup$scrollHeight !== void 0 ? _popup$scrollHeight : 0,
|
|
247
|
-
scroll
|
|
248
|
-
});
|
|
219
|
+
if (autoCorrectTopOverflow && chosenDirection && anchor) {
|
|
220
|
+
const maxForDirection = maxHeightForDirection(chosenDirection, anchor) - sidePadding;
|
|
221
|
+
if (!styles.maxHeight || styles.maxHeight > maxForDirection) {
|
|
222
|
+
styles.maxHeight = maxForDirection;
|
|
223
|
+
if (styles.top === 0) {
|
|
224
|
+
styles.top = sidePadding;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
249
227
|
}
|
|
250
228
|
if (minWidth === MinWidth.TARGET || minWidth === 'target') {
|
|
251
229
|
styles.minWidth = anchorRect.width;
|
|
@@ -166,7 +166,7 @@ class SelectPopup extends PureComponent {
|
|
|
166
166
|
const containerNode = getPopupContainer(ringPopupTarget) || document.documentElement;
|
|
167
167
|
return anchorNode != null ? Math.min(directions.reduce((maxHeight, direction) => {
|
|
168
168
|
var _maxHeightForDirectio;
|
|
169
|
-
return Math.max(maxHeight, (_maxHeightForDirectio = maxHeightForDirection(direction, anchorNode, getStyles(containerNode).position !== 'static' ? containerNode :
|
|
169
|
+
return Math.max(maxHeight, (_maxHeightForDirectio = maxHeightForDirection(direction, anchorNode, getStyles(containerNode).position !== 'static' ? containerNode : undefined)) !== null && _maxHeightForDirectio !== void 0 ? _maxHeightForDirectio : 0);
|
|
170
170
|
}, minMaxHeight), userDefinedMaxHeight) : userDefinedMaxHeight;
|
|
171
171
|
}));
|
|
172
172
|
_defineProperty(this, "popupRef", el => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jetbrains/ring-ui",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.1",
|
|
4
4
|
"description": "JetBrains UI library",
|
|
5
5
|
"author": "JetBrains",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"@babel/eslint-parser": "^7.19.1",
|
|
79
79
|
"@jetbrains/eslint-config": "^5.4.1",
|
|
80
80
|
"@jetbrains/stylelint-config": "^3.0.2",
|
|
81
|
-
"@primer/octicons": "^17.
|
|
81
|
+
"@primer/octicons": "^17.11.1",
|
|
82
82
|
"@rollup/plugin-babel": "^6.0.3",
|
|
83
83
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
|
84
84
|
"@rollup/plugin-replace": "^5.0.2",
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
"husky": "^8.0.3",
|
|
138
138
|
"identity-obj-proxy": "^3.0.0",
|
|
139
139
|
"imports-loader": "^4.0.1",
|
|
140
|
-
"jest": "~29.
|
|
140
|
+
"jest": "~29.4.0",
|
|
141
141
|
"jest-environment-jsdom": "^29.4.0",
|
|
142
142
|
"jest-teamcity": "^1.10.0",
|
|
143
143
|
"karma": "^6.4.1",
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
"merge-options": "^3.0.4",
|
|
151
151
|
"mocha": "^10.2.0",
|
|
152
152
|
"pinst": "^3.0.0",
|
|
153
|
-
"puppeteer": "^19.6.
|
|
153
|
+
"puppeteer": "^19.6.1",
|
|
154
154
|
"raw-loader": "^4.0.2",
|
|
155
155
|
"react": "^18.2.0",
|
|
156
156
|
"react-dom": "^18.2.0",
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import angular from 'angular';
|
|
2
|
-
|
|
3
|
-
import angularDecorator, {APP_NAME} from '../../.storybook/angular-decorator';
|
|
4
|
-
|
|
5
|
-
import LinkNG from '../link-ng/link-ng';
|
|
6
|
-
|
|
7
|
-
import AnalyticsNG from './analytics-ng';
|
|
8
|
-
|
|
9
|
-
export default {
|
|
10
|
-
title: 'Legacy Angular/Analytics Ng',
|
|
11
|
-
decorators: [angularDecorator()],
|
|
12
|
-
|
|
13
|
-
parameters: {
|
|
14
|
-
notes: 'Provides an Angular wrapper for Analytics.',
|
|
15
|
-
hermione: {skip: true}
|
|
16
|
-
},
|
|
17
|
-
argTypes: {onAnalytics: {}}
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
function noop() {}
|
|
21
|
-
export const analyticsStory = ({onAnalytics = noop}) => {
|
|
22
|
-
angular.
|
|
23
|
-
module(APP_NAME, [AnalyticsNG, LinkNG]).
|
|
24
|
-
config(function config(analyticsProvider, AnalyticsCustomPlugin /*, AnalyticsGAPlugin*/) {
|
|
25
|
-
const analyticsEnabled = true;
|
|
26
|
-
if (analyticsEnabled) {
|
|
27
|
-
const isDevelopment = true;
|
|
28
|
-
const customPlugin = new AnalyticsCustomPlugin(
|
|
29
|
-
data => onAnalytics('Here you can send data to server', data),
|
|
30
|
-
isDevelopment,
|
|
31
|
-
600
|
|
32
|
-
);
|
|
33
|
-
// const gaId = 'GA-XXXXX-ID';
|
|
34
|
-
analyticsProvider.plugins([
|
|
35
|
-
customPlugin //, new AnalyticsGAPlugin(gaId)
|
|
36
|
-
]);
|
|
37
|
-
}
|
|
38
|
-
}).
|
|
39
|
-
controller('TrackEventDemoCtrl', function controller(analytics) {
|
|
40
|
-
analytics.trackEvent('track-event-demo', 'show');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
return `
|
|
44
|
-
<div>
|
|
45
|
-
<p>Hover or click the links below and check the console output:</p>
|
|
46
|
-
<div>
|
|
47
|
-
<rg-link href="" rg-analytics="overview:view-doc">
|
|
48
|
-
Link with an onclick analytics trigger
|
|
49
|
-
</rg-link>
|
|
50
|
-
</div>
|
|
51
|
-
<div>
|
|
52
|
-
<rg-link href="" rg-analytics="overview:view-doc" rg-analytics-on="mouseover">
|
|
53
|
-
Link with an onmouseover analytics trigger
|
|
54
|
-
</rg-link>
|
|
55
|
-
</div>
|
|
56
|
-
<div ng-controller="TrackEventDemoCtrl"></div>
|
|
57
|
-
</div>
|
|
58
|
-
`;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
analyticsStory.storyName = 'Analytics Ng';
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import angular from 'angular';
|
|
2
|
-
|
|
3
|
-
import analyticsInstance from '../analytics/analytics';
|
|
4
|
-
import AnalyticsGAPlugin from '../analytics/analytics__ga-plugin';
|
|
5
|
-
import AnalyticsFUSPlugin from '../analytics/analytics__fus-plugin';
|
|
6
|
-
import AnalyticsCustomPlugin from '../analytics/analytics__custom-plugin';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @name Analytics Ng
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const angularModule = angular.module('Ring.analytics', []);
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @name analyticsProvider
|
|
16
|
-
* @description Configures analytics with plugins.
|
|
17
|
-
*/
|
|
18
|
-
angularModule.provider('analytics', function provider() {
|
|
19
|
-
let configPlugins = [];
|
|
20
|
-
/**
|
|
21
|
-
* @param plugins
|
|
22
|
-
*/
|
|
23
|
-
this.plugins = plugins => {
|
|
24
|
-
configPlugins = plugins;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
this.$get = function get($log, $injector) {
|
|
28
|
-
const loadedPlugins = [];
|
|
29
|
-
for (let i = 0; i < configPlugins.length; ++i) {
|
|
30
|
-
if (typeof configPlugins[i] === 'string') {
|
|
31
|
-
try {
|
|
32
|
-
const plugin = $injector.get(configPlugins[i]);
|
|
33
|
-
loadedPlugins.push(plugin);
|
|
34
|
-
$log.debug(`analytics: loaded plugin ${configPlugins[i]}`);
|
|
35
|
-
} catch (err) {
|
|
36
|
-
$log.debug(`analytics: unable to load factory ${configPlugins[i]}`);
|
|
37
|
-
}
|
|
38
|
-
} else {
|
|
39
|
-
loadedPlugins.push(configPlugins[i]);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
analyticsInstance.config(loadedPlugins);
|
|
43
|
-
return analyticsInstance;
|
|
44
|
-
};
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
angularModule.constant('AnalyticsGAPlugin', AnalyticsGAPlugin);
|
|
48
|
-
angularModule.constant('AnalyticsCustomPlugin', AnalyticsCustomPlugin);
|
|
49
|
-
angularModule.constant('AnalyticsFUSPlugin', AnalyticsFUSPlugin);
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Enable page tracking
|
|
53
|
-
*/
|
|
54
|
-
angularModule.run(function analyticsRun($rootScope, analytics) {
|
|
55
|
-
$rootScope.$on('$routeChangeSuccess', (evt, current) => { // eslint-disable-line angular/on-watch
|
|
56
|
-
/* eslint-disable angular/no-private-call */
|
|
57
|
-
if (current && current.$$route && current.$$route.originalPath) {
|
|
58
|
-
analytics.trackPageView(current.$$route.originalPath);
|
|
59
|
-
}
|
|
60
|
-
/* eslint-enable angular/no-private-call */
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* @ngdoc directive
|
|
66
|
-
* @name rg-analytics
|
|
67
|
-
*
|
|
68
|
-
* @description
|
|
69
|
-
* The `rg-analytics="<categoryName>:<eventName>"` sends categoryName and eventName to analytics server on
|
|
70
|
-
* user action, specified via attribute `rg-analytics-on` (e.g. rg-analytics-on='mouseover' means that analytics will be sent on mouseover,
|
|
71
|
-
* rg-analytics-on='click' - on click). If there is no attribute rg-analytics-on, the default value 'click' is used.
|
|
72
|
-
*/
|
|
73
|
-
angularModule.directive('rgAnalytics', function rgAnalyticsDirective(analytics) {
|
|
74
|
-
return {
|
|
75
|
-
restrict: 'A',
|
|
76
|
-
replace: false,
|
|
77
|
-
|
|
78
|
-
link: function link($scope, elem) {
|
|
79
|
-
const eventType = elem.attr('rg-analytics-on') || 'click';
|
|
80
|
-
angular.element(elem).bind(eventType, () => {
|
|
81
|
-
analytics.track(elem.attr('rg-analytics'));
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
export default angularModule.name;
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import angular from 'angular';
|
|
2
|
-
import 'angular-mocks';
|
|
3
|
-
|
|
4
|
-
import AnalyticsCustomPlugin from '../analytics/analytics__custom-plugin';
|
|
5
|
-
|
|
6
|
-
import Analytics from './analytics-ng';
|
|
7
|
-
|
|
8
|
-
describe('Analytics Ng', () => {
|
|
9
|
-
beforeEach(
|
|
10
|
-
window.module('Ring.analytics',
|
|
11
|
-
analyticsProvider => {
|
|
12
|
-
const send = sandbox.stub();
|
|
13
|
-
analyticsProvider.plugins([new AnalyticsCustomPlugin(send)]);
|
|
14
|
-
})
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
/* global inject */
|
|
18
|
-
it('should define module', inject(() => {
|
|
19
|
-
angular.module(Analytics).should.exist;
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
it('should export factory', inject(analytics => {
|
|
23
|
-
analytics.should.exist;
|
|
24
|
-
}));
|
|
25
|
-
|
|
26
|
-
it('should export correct interface', inject(analytics => {
|
|
27
|
-
analytics.trackPageView.should.exist;
|
|
28
|
-
analytics.trackEvent.should.exist;
|
|
29
|
-
analytics.track.should.exist;
|
|
30
|
-
}));
|
|
31
|
-
|
|
32
|
-
describe('rgAnalytics', () => {
|
|
33
|
-
let $rootScope;
|
|
34
|
-
let $compile;
|
|
35
|
-
let analytics;
|
|
36
|
-
|
|
37
|
-
beforeEach(inject((_$rootScope_, _$compile_, _analytics_) => {
|
|
38
|
-
$rootScope = _$rootScope_;
|
|
39
|
-
$compile = _$compile_;
|
|
40
|
-
analytics = _analytics_;
|
|
41
|
-
sandbox.spy(analytics, 'trackEvent');
|
|
42
|
-
}));
|
|
43
|
-
|
|
44
|
-
function compileTemplate(template) {
|
|
45
|
-
const elem = $compile(template)($rootScope);
|
|
46
|
-
$rootScope.$digest();
|
|
47
|
-
return elem;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const click = new CustomEvent('click');
|
|
51
|
-
|
|
52
|
-
it('should track event for full arguments list', () => {
|
|
53
|
-
const elem = compileTemplate('<a href="#" rg-analytics="some-category:some-event">Link</a>');
|
|
54
|
-
elem[0].dispatchEvent(click);
|
|
55
|
-
|
|
56
|
-
analytics.trackEvent.should.calledWith('some-category', 'some-event');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should track event (with empty event)', () => {
|
|
60
|
-
const elem = compileTemplate('<a href="#" rg-analytics="some-category">Link</a>');
|
|
61
|
-
elem[0].dispatchEvent(click);
|
|
62
|
-
|
|
63
|
-
analytics.trackEvent.should.calledWith('some-category', '');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should track event (with empty category)', () => {
|
|
67
|
-
const elem = compileTemplate('<a href="#" rg-analytics=":some-event">Link</a>');
|
|
68
|
-
elem[0].dispatchEvent(click);
|
|
69
|
-
|
|
70
|
-
analytics.trackEvent.should.calledWith('', 'some-event');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should track event for non-default user action)', () => {
|
|
74
|
-
const elem = compileTemplate(
|
|
75
|
-
'<a href="#" rg-analytics="category:expand" rg-analytics-on="blur">Link</a>'
|
|
76
|
-
);
|
|
77
|
-
elem[0].dispatchEvent(new CustomEvent('blur'));
|
|
78
|
-
|
|
79
|
-
analytics.trackEvent.should.calledWith('category', 'expand');
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
});
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import angular from 'angular';
|
|
2
|
-
import analyticsInstance from '../analytics/analytics.js';
|
|
3
|
-
import AnalyticsGAPlugin from '../analytics/analytics__ga-plugin.js';
|
|
4
|
-
import AnalyticsFUSPlugin from '../analytics/analytics__fus-plugin.js';
|
|
5
|
-
import AnalyticsCustomPlugin from '../analytics/analytics__custom-plugin.js';
|
|
6
|
-
import '../_helpers/_rollupPluginBabelHelpers.js';
|
|
7
|
-
import '../analytics/analytics__plugin-utils.js';
|
|
8
|
-
import '../global/sniffer.js';
|
|
9
|
-
import 'sniffr';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @name Analytics Ng
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const angularModule = angular.module('Ring.analytics', []);
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @name analyticsProvider
|
|
19
|
-
* @description Configures analytics with plugins.
|
|
20
|
-
*/
|
|
21
|
-
angularModule.provider('analytics', function provider() {
|
|
22
|
-
let configPlugins = [];
|
|
23
|
-
/**
|
|
24
|
-
* @param plugins
|
|
25
|
-
*/
|
|
26
|
-
this.plugins = plugins => {
|
|
27
|
-
configPlugins = plugins;
|
|
28
|
-
};
|
|
29
|
-
this.$get = ["$log", "$injector", function get($log, $injector) {
|
|
30
|
-
const loadedPlugins = [];
|
|
31
|
-
for (let i = 0; i < configPlugins.length; ++i) {
|
|
32
|
-
if (typeof configPlugins[i] === 'string') {
|
|
33
|
-
try {
|
|
34
|
-
const plugin = $injector.get(configPlugins[i]);
|
|
35
|
-
loadedPlugins.push(plugin);
|
|
36
|
-
$log.debug(`analytics: loaded plugin ${configPlugins[i]}`);
|
|
37
|
-
} catch (err) {
|
|
38
|
-
$log.debug(`analytics: unable to load factory ${configPlugins[i]}`);
|
|
39
|
-
}
|
|
40
|
-
} else {
|
|
41
|
-
loadedPlugins.push(configPlugins[i]);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
analyticsInstance.config(loadedPlugins);
|
|
45
|
-
return analyticsInstance;
|
|
46
|
-
}];
|
|
47
|
-
});
|
|
48
|
-
angularModule.constant('AnalyticsGAPlugin', AnalyticsGAPlugin);
|
|
49
|
-
angularModule.constant('AnalyticsCustomPlugin', AnalyticsCustomPlugin);
|
|
50
|
-
angularModule.constant('AnalyticsFUSPlugin', AnalyticsFUSPlugin);
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Enable page tracking
|
|
54
|
-
*/
|
|
55
|
-
angularModule.run(["$rootScope", "analytics", function analyticsRun($rootScope, analytics) {
|
|
56
|
-
$rootScope.$on('$routeChangeSuccess', (evt, current) => {
|
|
57
|
-
// eslint-disable-line angular/on-watch
|
|
58
|
-
/* eslint-disable angular/no-private-call */
|
|
59
|
-
if (current && current.$$route && current.$$route.originalPath) {
|
|
60
|
-
analytics.trackPageView(current.$$route.originalPath);
|
|
61
|
-
}
|
|
62
|
-
/* eslint-enable angular/no-private-call */
|
|
63
|
-
});
|
|
64
|
-
}]);
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* @ngdoc directive
|
|
68
|
-
* @name rg-analytics
|
|
69
|
-
*
|
|
70
|
-
* @description
|
|
71
|
-
* The `rg-analytics="<categoryName>:<eventName>"` sends categoryName and eventName to analytics server on
|
|
72
|
-
* user action, specified via attribute `rg-analytics-on` (e.g. rg-analytics-on='mouseover' means that analytics will be sent on mouseover,
|
|
73
|
-
* rg-analytics-on='click' - on click). If there is no attribute rg-analytics-on, the default value 'click' is used.
|
|
74
|
-
*/
|
|
75
|
-
angularModule.directive('rgAnalytics', ["analytics", function rgAnalyticsDirective(analytics) {
|
|
76
|
-
return {
|
|
77
|
-
restrict: 'A',
|
|
78
|
-
replace: false,
|
|
79
|
-
link: function link($scope, elem) {
|
|
80
|
-
const eventType = elem.attr('rg-analytics-on') || 'click';
|
|
81
|
-
angular.element(elem).bind(eventType, () => {
|
|
82
|
-
analytics.track(elem.attr('rg-analytics'));
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
}]);
|
|
87
|
-
var analyticsNg = angularModule.name;
|
|
88
|
-
|
|
89
|
-
export { analyticsNg as default };
|