@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.
@@ -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, containerNode?: Element | null): number | null;
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.popup.clientHeight;
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 handleTopOffScreen({ sidePadding, styles, anchorRect, maxHeight, popupScrollHeight, direction, scroll }) {
80
- const BORDER_COMPENSATION = 1;
81
- const { TOP_LEFT, TOP_RIGHT, TOP_CENTER, RIGHT_TOP, LEFT_TOP } = Directions;
82
- const openedToTop = direction != null && [TOP_LEFT, TOP_RIGHT, TOP_CENTER, RIGHT_TOP, LEFT_TOP].includes(direction);
83
- if (!openedToTop) {
84
- return styles;
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
- const isAttachedToAnchorTop = direction != null && [TOP_LEFT, TOP_CENTER, TOP_RIGHT].includes(direction);
87
- const attachingPointY = (isAttachedToAnchorTop ? anchorRect.top : anchorRect.bottom);
88
- const effectiveHeight = maxHeight && typeof maxHeight === 'number'
89
- ? Math.min(popupScrollHeight, maxHeight)
90
- : popupScrollHeight;
91
- const hypotheticalTop = attachingPointY - effectiveHeight;
92
- if (hypotheticalTop <= sidePadding) {
93
- styles.top = sidePadding + scroll.top;
94
- styles.maxHeight = attachingPointY - sidePadding + BORDER_COMPENSATION;
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
- return null;
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
- styles = handleTopOffScreen({
183
- sidePadding,
184
- styles,
185
- anchorRect,
186
- maxHeight,
187
- direction: chosenDirection,
188
- popupScrollHeight: popup?.scrollHeight ?? 0,
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 : null) ?? 0)), minMaxHeight), userDefinedMaxHeight)
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) => {
@@ -84,6 +84,6 @@ class Analytics {
84
84
  return suffix;
85
85
  }
86
86
  }
87
- var analyticsInstance = new Analytics();
87
+ var analytics = new Analytics();
88
88
 
89
- export { Analytics, analyticsInstance as default };
89
+ export { Analytics, analytics as default };
@@ -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, containerNode?: Element | null): number | null;
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;
@@ -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.clientHeight;
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 handleTopOffScreen(_ref) {
105
- let {
106
- sidePadding,
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
- const containerRect = container.getBoundingClientRect();
140
- const topMaxHeight = Math.max(domRect.top - containerRect.top, 0);
141
- const containerHeight = Math.max(containerRect.height,
142
- // XXX
143
- // If container is the document element
144
- // then we check client height too because we may have situation when
145
- // "height" from "getBoundingClientRect" less then "clientHeight".
146
- container === document.documentElement ? container.clientHeight : 0);
147
- const bottomMaxHeight = Math.max(containerHeight - (topMaxHeight + domRect.height), 0);
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
- return null;
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 = directionsMatrix[directions[0]];
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((_ref2, _ref3) => {
190
+ })).sort((_ref, _ref2) => {
212
191
  let {
213
192
  styles: stylesA
214
- } = _ref2;
193
+ } = _ref;
215
194
  let {
216
195
  styles: stylesB
217
- } = _ref3;
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 = sortedByIncreasingOverflow[0].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
- var _popup$scrollHeight;
240
- styles = handleTopOffScreen({
241
- sidePadding,
242
- styles,
243
- anchorRect,
244
- maxHeight,
245
- direction: chosenDirection,
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 : null)) !== null && _maxHeightForDirectio !== void 0 ? _maxHeightForDirectio : 0);
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.0.100",
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.10.2",
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.3.1",
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.0",
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 };