@pingux/astro 2.170.1-alpha.0 → 2.171.0-alpha.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/lib/cjs/components/Avatar/Avatar.js +13 -2
- package/lib/cjs/components/Avatar/Avatar.test.js +95 -1
- package/lib/cjs/components/Avatar/constants.d.ts +2 -0
- package/lib/cjs/components/Avatar/constants.js +8 -0
- package/lib/cjs/components/Avatar/getColorFromUuid.d.ts +2 -0
- package/lib/cjs/components/Avatar/getColorFromUuid.js +37 -0
- package/lib/cjs/types/avatar.d.ts +2 -0
- package/lib/components/Avatar/Avatar.js +14 -3
- package/lib/components/Avatar/Avatar.test.js +95 -1
- package/lib/components/Avatar/constants.js +2 -0
- package/lib/components/Avatar/getColorFromUuid.js +29 -0
- package/lib/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -21,8 +21,10 @@ var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime-c
|
|
|
21
21
|
var _react = _interopRequireWildcard(require("react"));
|
|
22
22
|
var _themeUi = require("theme-ui");
|
|
23
23
|
var _hooks = require("../../hooks");
|
|
24
|
+
var _constants = require("./constants");
|
|
25
|
+
var _getColorFromUuid = _interopRequireDefault(require("./getColorFromUuid"));
|
|
24
26
|
var _react2 = require("@emotion/react");
|
|
25
|
-
var _excluded = ["alt", "defaultText", "color", "className", "size", "src", "sx", "children", "isSquare", "isLogo"];
|
|
27
|
+
var _excluded = ["alt", "defaultText", "color", "colorId", "className", "size", "src", "sx", "children", "isSquare", "isLogo"];
|
|
26
28
|
function _interopRequireWildcard(e, t) { if ("function" == typeof _WeakMap) var r = new _WeakMap(), n = new _WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = _Object$defineProperty) && _Object$getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
27
29
|
function ownKeys(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
28
30
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context = ownKeys(Object(t), !0)).call(_context, function (r) { (0, _defineProperty2["default"])(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context2 = ownKeys(Object(t))).call(_context2, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
@@ -32,6 +34,7 @@ var Avatar = /*#__PURE__*/(0, _react.forwardRef)(function (props, ref) {
|
|
|
32
34
|
_props$defaultText = props.defaultText,
|
|
33
35
|
defaultText = _props$defaultText === void 0 ? 'AA' : _props$defaultText,
|
|
34
36
|
color = props.color,
|
|
37
|
+
colorId = props.colorId,
|
|
35
38
|
className = props.className,
|
|
36
39
|
_props$size = props.size,
|
|
37
40
|
size = _props$size === void 0 ? 'sm' : _props$size,
|
|
@@ -41,7 +44,15 @@ var Avatar = /*#__PURE__*/(0, _react.forwardRef)(function (props, ref) {
|
|
|
41
44
|
isSquare = props.isSquare,
|
|
42
45
|
isLogo = props.isLogo,
|
|
43
46
|
others = (0, _objectWithoutProperties2["default"])(props, _excluded);
|
|
44
|
-
|
|
47
|
+
|
|
48
|
+
// this will use color prop if provided,
|
|
49
|
+
// else will map colorId to a color, else defaults to 'green'
|
|
50
|
+
var finalColor = (0, _react.useMemo)(function () {
|
|
51
|
+
if (color) return color;
|
|
52
|
+
if (colorId) return (0, _getColorFromUuid["default"])(colorId, _constants.avatarColors);
|
|
53
|
+
return 'green';
|
|
54
|
+
}, [color, colorId, _constants.avatarColors]);
|
|
55
|
+
var _useStatusClasses = (0, _hooks.useStatusClasses)(className, (0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])({}, "is-".concat(finalColor), finalColor), "size-".concat(size), size), "font-size-".concat(size), src ? false : size), 'is-square', isSquare), 'is-image', src), 'is-logo', isLogo)),
|
|
45
56
|
classNames = _useStatusClasses.classNames;
|
|
46
57
|
if (src) {
|
|
47
58
|
return (0, _react2.jsx)(_themeUi.Box, (0, _extends2["default"])({
|
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
|
|
4
|
+
var _from = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/array/from"));
|
|
5
|
+
var _find = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/find"));
|
|
6
|
+
var _startsWith = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/starts-with"));
|
|
7
|
+
var _forEach = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/for-each"));
|
|
4
8
|
var _extends2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/extends"));
|
|
5
9
|
var _react = _interopRequireDefault(require("react"));
|
|
6
10
|
var _faker = require("@faker-js/faker");
|
|
11
|
+
var _nodeCrypto = require("node:crypto");
|
|
7
12
|
var _testWrapper = require("../../utils/testUtils/testWrapper");
|
|
8
13
|
var _universalComponentTest = require("../../utils/testUtils/universalComponentTest");
|
|
14
|
+
var _constants = require("./constants");
|
|
15
|
+
var _getColorFromUuid = _interopRequireDefault(require("./getColorFromUuid"));
|
|
9
16
|
var _ = _interopRequireDefault(require("."));
|
|
10
17
|
var _react2 = require("@emotion/react");
|
|
11
18
|
var src = _faker.faker.image.lorempicsum.imageUrl(150, 150, false, undefined, '1');
|
|
19
|
+
var datatestId = 'avatar-component';
|
|
12
20
|
var defaultProps = {
|
|
13
|
-
src: src
|
|
21
|
+
src: src,
|
|
22
|
+
'data-testid': datatestId
|
|
14
23
|
};
|
|
15
24
|
var getComponent = function getComponent() {
|
|
16
25
|
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
@@ -43,4 +52,89 @@ test('an avatar is rendered with custom alt', function () {
|
|
|
43
52
|
});
|
|
44
53
|
var avatar = _testWrapper.screen.getByText('KL');
|
|
45
54
|
expect(avatar).toBeInTheDocument();
|
|
55
|
+
});
|
|
56
|
+
describe('getColorFromUUID', function () {
|
|
57
|
+
test('returns a consistent color for the same UUID', function () {
|
|
58
|
+
var uuid = '123e4567-e89b-12d3-a456-426614174000';
|
|
59
|
+
var result1 = (0, _getColorFromUuid["default"])(uuid, _constants.avatarColors);
|
|
60
|
+
var result2 = (0, _getColorFromUuid["default"])(uuid, _constants.avatarColors);
|
|
61
|
+
expect(result1).toBe(result2);
|
|
62
|
+
});
|
|
63
|
+
test('returns a color from the provided array', function () {
|
|
64
|
+
var uuid = 'random-id';
|
|
65
|
+
var result = (0, _getColorFromUuid["default"])(uuid, _constants.avatarColors);
|
|
66
|
+
expect(_constants.avatarColors).toContain(result);
|
|
67
|
+
});
|
|
68
|
+
test('distributes colors differently for different UUIDs', function () {
|
|
69
|
+
var colorA = (0, _getColorFromUuid["default"])('id-1', _constants.avatarColors);
|
|
70
|
+
var colorB = (0, _getColorFromUuid["default"])('id-2', _constants.avatarColors);
|
|
71
|
+
|
|
72
|
+
// While collisions are mathematically possible, for 2 items in a 10-item list,
|
|
73
|
+
// these specific IDs yield different results in FNV-1a.
|
|
74
|
+
expect(colorA).not.toBe(colorB);
|
|
75
|
+
});
|
|
76
|
+
test('throws error if color array is empty', function () {
|
|
77
|
+
expect(function () {
|
|
78
|
+
return (0, _getColorFromUuid["default"])('uuid', []);
|
|
79
|
+
}).toThrow('Color array cannot be empty.');
|
|
80
|
+
});
|
|
81
|
+
test('uses the specific color class when color prop is provided', function () {
|
|
82
|
+
getComponent({
|
|
83
|
+
color: 'blue',
|
|
84
|
+
colorId: 'some-id'
|
|
85
|
+
});
|
|
86
|
+
var avatar = _testWrapper.screen.getByTestId(datatestId);
|
|
87
|
+
|
|
88
|
+
// Checking for 'is-blue'
|
|
89
|
+
expect(avatar).toHaveClass('is-blue');
|
|
90
|
+
});
|
|
91
|
+
test('applies a hashed color class from colorId', function () {
|
|
92
|
+
var uuid = '550e8400-e29b-41d4-a716-446655440000';
|
|
93
|
+
getComponent({
|
|
94
|
+
colorId: uuid
|
|
95
|
+
});
|
|
96
|
+
var avatar = _testWrapper.screen.getByTestId(datatestId);
|
|
97
|
+
|
|
98
|
+
// We check that it has *a* class starting with 'is-'
|
|
99
|
+
// and specifically isn't the default 'is-green'
|
|
100
|
+
var classList = (0, _from["default"])(avatar.classList);
|
|
101
|
+
var colorClass = (0, _find["default"])(classList).call(classList, function (cls) {
|
|
102
|
+
return (0, _startsWith["default"])(cls).call(cls, 'is-');
|
|
103
|
+
});
|
|
104
|
+
expect(colorClass).toBeDefined();
|
|
105
|
+
expect(avatar).not.toHaveClass('is-green');
|
|
106
|
+
});
|
|
107
|
+
test('defaults to is-green when no color props are passed', function () {
|
|
108
|
+
getComponent();
|
|
109
|
+
var avatar = _testWrapper.screen.getByTestId(datatestId);
|
|
110
|
+
expect(avatar).toHaveClass('is-green');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('getColorFromUUID Distribution', function () {
|
|
114
|
+
test('distributes 10,000 UUIDs evenly across 10 colors', function () {
|
|
115
|
+
var iterations = 10000;
|
|
116
|
+
var distribution = {};
|
|
117
|
+
|
|
118
|
+
// Initialize counts
|
|
119
|
+
(0, _forEach["default"])(_constants.avatarColors).call(_constants.avatarColors, function (c) {
|
|
120
|
+
distribution[c] = 0;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Generate and hash
|
|
124
|
+
for (var i = 0; i < iterations; i += 1) {
|
|
125
|
+
// Use the imported randomUUID function directly
|
|
126
|
+
var uuid = (0, _nodeCrypto.randomUUID)();
|
|
127
|
+
var selectedColor = (0, _getColorFromUuid["default"])(uuid, _constants.avatarColors);
|
|
128
|
+
distribution[selectedColor] += 1;
|
|
129
|
+
}
|
|
130
|
+
var expectedMean = iterations / _constants.avatarColors.length;
|
|
131
|
+
// 15% variance is a safe threshold for 10k iterations
|
|
132
|
+
var allowedVariance = 0.15;
|
|
133
|
+
(0, _forEach["default"])(_constants.avatarColors).call(_constants.avatarColors, function (color) {
|
|
134
|
+
var count = distribution[color];
|
|
135
|
+
// Assert that each color is roughly 10% of the total
|
|
136
|
+
expect(count).toBeGreaterThan(expectedMean * (1 - allowedVariance));
|
|
137
|
+
expect(count).toBeLessThan(expectedMean * (1 + allowedVariance));
|
|
138
|
+
});
|
|
139
|
+
});
|
|
46
140
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property");
|
|
4
|
+
_Object$defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.avatarColors = void 0;
|
|
8
|
+
var avatarColors = exports.avatarColors = ['green', 'purple', 'pink', 'red', 'orange', 'yellow', 'teal', 'cyan', 'blue', 'indigo'];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property");
|
|
4
|
+
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
|
|
5
|
+
_Object$defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports["default"] = void 0;
|
|
9
|
+
var _imul = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/math/imul"));
|
|
10
|
+
// * Maps a UUID to a specific color from a provided array.
|
|
11
|
+
// * @param uuid - The unique identifier string.
|
|
12
|
+
// * @param colors - An array of color strings/objects.
|
|
13
|
+
// * @returns A single color from the array.
|
|
14
|
+
|
|
15
|
+
var getColorFromUUID = function getColorFromUUID(uuid, colors) {
|
|
16
|
+
if (colors.length === 0) {
|
|
17
|
+
throw new Error('Color array cannot be empty.');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// FNV-1a Hash implementation
|
|
21
|
+
// This spreads the bits of the UUID string evenly across a 32-bit integer
|
|
22
|
+
var hash = 2166136261;
|
|
23
|
+
for (var i = 0; i < uuid.length; i += 1) {
|
|
24
|
+
/* eslint-disable no-bitwise */
|
|
25
|
+
hash ^= uuid.charCodeAt(i);
|
|
26
|
+
// Standard FNV prime (using Math.imul for 32-bit integer multiplication)
|
|
27
|
+
|
|
28
|
+
hash = (0, _imul["default"])(hash, 16777619);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Convert to an unsigned index using the modulo operator
|
|
32
|
+
// The >>> 0 ensures we are dealing with an unsigned 32-bit integer
|
|
33
|
+
/* eslint-disable no-bitwise */
|
|
34
|
+
var index = (hash >>> 0) % colors.length;
|
|
35
|
+
return colors[index];
|
|
36
|
+
};
|
|
37
|
+
var _default = exports["default"] = getColorFromUUID;
|
|
@@ -9,12 +9,14 @@ import _Object$defineProperty from "@babel/runtime-corejs3/core-js-stable/object
|
|
|
9
9
|
import _extends from "@babel/runtime-corejs3/helpers/esm/extends";
|
|
10
10
|
import _defineProperty from "@babel/runtime-corejs3/helpers/esm/defineProperty";
|
|
11
11
|
import _objectWithoutProperties from "@babel/runtime-corejs3/helpers/esm/objectWithoutProperties";
|
|
12
|
-
var _excluded = ["alt", "defaultText", "color", "className", "size", "src", "sx", "children", "isSquare", "isLogo"];
|
|
12
|
+
var _excluded = ["alt", "defaultText", "color", "colorId", "className", "size", "src", "sx", "children", "isSquare", "isLogo"];
|
|
13
13
|
function ownKeys(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
14
14
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context = ownKeys(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context2 = ownKeys(Object(t))).call(_context2, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
15
|
-
import React, { forwardRef } from 'react';
|
|
15
|
+
import React, { forwardRef, useMemo } from 'react';
|
|
16
16
|
import { Avatar as ThemeUIAvatar, Box } from 'theme-ui';
|
|
17
17
|
import { useStatusClasses } from '../../hooks';
|
|
18
|
+
import { avatarColors } from './constants';
|
|
19
|
+
import getColorFromUUID from './getColorFromUuid';
|
|
18
20
|
import { jsx as ___EmotionJSX } from "@emotion/react";
|
|
19
21
|
var Avatar = /*#__PURE__*/forwardRef(function (props, ref) {
|
|
20
22
|
var _props$alt = props.alt,
|
|
@@ -22,6 +24,7 @@ var Avatar = /*#__PURE__*/forwardRef(function (props, ref) {
|
|
|
22
24
|
_props$defaultText = props.defaultText,
|
|
23
25
|
defaultText = _props$defaultText === void 0 ? 'AA' : _props$defaultText,
|
|
24
26
|
color = props.color,
|
|
27
|
+
colorId = props.colorId,
|
|
25
28
|
className = props.className,
|
|
26
29
|
_props$size = props.size,
|
|
27
30
|
size = _props$size === void 0 ? 'sm' : _props$size,
|
|
@@ -31,7 +34,15 @@ var Avatar = /*#__PURE__*/forwardRef(function (props, ref) {
|
|
|
31
34
|
isSquare = props.isSquare,
|
|
32
35
|
isLogo = props.isLogo,
|
|
33
36
|
others = _objectWithoutProperties(props, _excluded);
|
|
34
|
-
|
|
37
|
+
|
|
38
|
+
// this will use color prop if provided,
|
|
39
|
+
// else will map colorId to a color, else defaults to 'green'
|
|
40
|
+
var finalColor = useMemo(function () {
|
|
41
|
+
if (color) return color;
|
|
42
|
+
if (colorId) return getColorFromUUID(colorId, avatarColors);
|
|
43
|
+
return 'green';
|
|
44
|
+
}, [color, colorId, avatarColors]);
|
|
45
|
+
var _useStatusClasses = useStatusClasses(className, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, "is-".concat(finalColor), finalColor), "size-".concat(size), size), "font-size-".concat(size), src ? false : size), 'is-square', isSquare), 'is-image', src), 'is-logo', isLogo)),
|
|
35
46
|
classNames = _useStatusClasses.classNames;
|
|
36
47
|
if (src) {
|
|
37
48
|
return ___EmotionJSX(Box, _extends({
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import _extends from "@babel/runtime-corejs3/helpers/esm/extends";
|
|
2
|
+
import _Array$from from "@babel/runtime-corejs3/core-js-stable/array/from";
|
|
3
|
+
import _findInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/find";
|
|
4
|
+
import _startsWithInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/starts-with";
|
|
5
|
+
import _forEachInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/for-each";
|
|
2
6
|
import React from 'react';
|
|
3
7
|
import { faker } from '@faker-js/faker';
|
|
8
|
+
import { randomUUID } from 'node:crypto';
|
|
4
9
|
import { render, screen } from '../../utils/testUtils/testWrapper';
|
|
5
10
|
import { universalComponentTests } from '../../utils/testUtils/universalComponentTest';
|
|
11
|
+
import { avatarColors as colors } from './constants';
|
|
12
|
+
import getColorFromUUID from './getColorFromUuid';
|
|
6
13
|
import Avatar from '.';
|
|
7
14
|
import { jsx as ___EmotionJSX } from "@emotion/react";
|
|
8
15
|
var src = faker.image.lorempicsum.imageUrl(150, 150, false, undefined, '1');
|
|
16
|
+
var datatestId = 'avatar-component';
|
|
9
17
|
var defaultProps = {
|
|
10
|
-
src: src
|
|
18
|
+
src: src,
|
|
19
|
+
'data-testid': datatestId
|
|
11
20
|
};
|
|
12
21
|
var getComponent = function getComponent() {
|
|
13
22
|
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
@@ -40,4 +49,89 @@ test('an avatar is rendered with custom alt', function () {
|
|
|
40
49
|
});
|
|
41
50
|
var avatar = screen.getByText('KL');
|
|
42
51
|
expect(avatar).toBeInTheDocument();
|
|
52
|
+
});
|
|
53
|
+
describe('getColorFromUUID', function () {
|
|
54
|
+
test('returns a consistent color for the same UUID', function () {
|
|
55
|
+
var uuid = '123e4567-e89b-12d3-a456-426614174000';
|
|
56
|
+
var result1 = getColorFromUUID(uuid, colors);
|
|
57
|
+
var result2 = getColorFromUUID(uuid, colors);
|
|
58
|
+
expect(result1).toBe(result2);
|
|
59
|
+
});
|
|
60
|
+
test('returns a color from the provided array', function () {
|
|
61
|
+
var uuid = 'random-id';
|
|
62
|
+
var result = getColorFromUUID(uuid, colors);
|
|
63
|
+
expect(colors).toContain(result);
|
|
64
|
+
});
|
|
65
|
+
test('distributes colors differently for different UUIDs', function () {
|
|
66
|
+
var colorA = getColorFromUUID('id-1', colors);
|
|
67
|
+
var colorB = getColorFromUUID('id-2', colors);
|
|
68
|
+
|
|
69
|
+
// While collisions are mathematically possible, for 2 items in a 10-item list,
|
|
70
|
+
// these specific IDs yield different results in FNV-1a.
|
|
71
|
+
expect(colorA).not.toBe(colorB);
|
|
72
|
+
});
|
|
73
|
+
test('throws error if color array is empty', function () {
|
|
74
|
+
expect(function () {
|
|
75
|
+
return getColorFromUUID('uuid', []);
|
|
76
|
+
}).toThrow('Color array cannot be empty.');
|
|
77
|
+
});
|
|
78
|
+
test('uses the specific color class when color prop is provided', function () {
|
|
79
|
+
getComponent({
|
|
80
|
+
color: 'blue',
|
|
81
|
+
colorId: 'some-id'
|
|
82
|
+
});
|
|
83
|
+
var avatar = screen.getByTestId(datatestId);
|
|
84
|
+
|
|
85
|
+
// Checking for 'is-blue'
|
|
86
|
+
expect(avatar).toHaveClass('is-blue');
|
|
87
|
+
});
|
|
88
|
+
test('applies a hashed color class from colorId', function () {
|
|
89
|
+
var uuid = '550e8400-e29b-41d4-a716-446655440000';
|
|
90
|
+
getComponent({
|
|
91
|
+
colorId: uuid
|
|
92
|
+
});
|
|
93
|
+
var avatar = screen.getByTestId(datatestId);
|
|
94
|
+
|
|
95
|
+
// We check that it has *a* class starting with 'is-'
|
|
96
|
+
// and specifically isn't the default 'is-green'
|
|
97
|
+
var classList = _Array$from(avatar.classList);
|
|
98
|
+
var colorClass = _findInstanceProperty(classList).call(classList, function (cls) {
|
|
99
|
+
return _startsWithInstanceProperty(cls).call(cls, 'is-');
|
|
100
|
+
});
|
|
101
|
+
expect(colorClass).toBeDefined();
|
|
102
|
+
expect(avatar).not.toHaveClass('is-green');
|
|
103
|
+
});
|
|
104
|
+
test('defaults to is-green when no color props are passed', function () {
|
|
105
|
+
getComponent();
|
|
106
|
+
var avatar = screen.getByTestId(datatestId);
|
|
107
|
+
expect(avatar).toHaveClass('is-green');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
describe('getColorFromUUID Distribution', function () {
|
|
111
|
+
test('distributes 10,000 UUIDs evenly across 10 colors', function () {
|
|
112
|
+
var iterations = 10000;
|
|
113
|
+
var distribution = {};
|
|
114
|
+
|
|
115
|
+
// Initialize counts
|
|
116
|
+
_forEachInstanceProperty(colors).call(colors, function (c) {
|
|
117
|
+
distribution[c] = 0;
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Generate and hash
|
|
121
|
+
for (var i = 0; i < iterations; i += 1) {
|
|
122
|
+
// Use the imported randomUUID function directly
|
|
123
|
+
var uuid = randomUUID();
|
|
124
|
+
var selectedColor = getColorFromUUID(uuid, colors);
|
|
125
|
+
distribution[selectedColor] += 1;
|
|
126
|
+
}
|
|
127
|
+
var expectedMean = iterations / colors.length;
|
|
128
|
+
// 15% variance is a safe threshold for 10k iterations
|
|
129
|
+
var allowedVariance = 0.15;
|
|
130
|
+
_forEachInstanceProperty(colors).call(colors, function (color) {
|
|
131
|
+
var count = distribution[color];
|
|
132
|
+
// Assert that each color is roughly 10% of the total
|
|
133
|
+
expect(count).toBeGreaterThan(expectedMean * (1 - allowedVariance));
|
|
134
|
+
expect(count).toBeLessThan(expectedMean * (1 + allowedVariance));
|
|
135
|
+
});
|
|
136
|
+
});
|
|
43
137
|
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import _Math$imul from "@babel/runtime-corejs3/core-js-stable/math/imul";
|
|
2
|
+
// * Maps a UUID to a specific color from a provided array.
|
|
3
|
+
// * @param uuid - The unique identifier string.
|
|
4
|
+
// * @param colors - An array of color strings/objects.
|
|
5
|
+
// * @returns A single color from the array.
|
|
6
|
+
|
|
7
|
+
var getColorFromUUID = function getColorFromUUID(uuid, colors) {
|
|
8
|
+
if (colors.length === 0) {
|
|
9
|
+
throw new Error('Color array cannot be empty.');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// FNV-1a Hash implementation
|
|
13
|
+
// This spreads the bits of the UUID string evenly across a 32-bit integer
|
|
14
|
+
var hash = 2166136261;
|
|
15
|
+
for (var i = 0; i < uuid.length; i += 1) {
|
|
16
|
+
/* eslint-disable no-bitwise */
|
|
17
|
+
hash ^= uuid.charCodeAt(i);
|
|
18
|
+
// Standard FNV prime (using Math.imul for 32-bit integer multiplication)
|
|
19
|
+
|
|
20
|
+
hash = _Math$imul(hash, 16777619);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Convert to an unsigned index using the modulo operator
|
|
24
|
+
// The >>> 0 ensures we are dealing with an unsigned 32-bit integer
|
|
25
|
+
/* eslint-disable no-bitwise */
|
|
26
|
+
var index = (hash >>> 0) % colors.length;
|
|
27
|
+
return colors[index];
|
|
28
|
+
};
|
|
29
|
+
export default getColorFromUUID;
|