diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2360d2a73..68b00bbdf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,21 @@
+## [1.36.1](https://github.com/mParticle/aquarium/compare/v1.36.0...v1.36.1) (2024-12-06)
+
+### Bug Fixes
+
+- rename notification icons ([#508](https://github.com/mParticle/aquarium/issues/508)) ([98ea06f](https://github.com/mParticle/aquarium/commit/98ea06f1c0f66d7ca469f77158b1d4cf75cb8f6f))
+
+# [1.36.0](https://github.com/mParticle/aquarium/compare/v1.35.0...v1.36.0) (2024-11-28)
+
+### Bug Fixes
+
+- typography component updates ([#497](https://github.com/mParticle/aquarium/issues/497)) ([d32d390](https://github.com/mParticle/aquarium/commit/d32d3900c3d89406a0f88bccd967ed356e8edfe5))
+
+### Features
+
+- add abSplit icon ([#499](https://github.com/mParticle/aquarium/issues/499)) ([8776f1c](https://github.com/mParticle/aquarium/commit/8776f1c2fced9cc75af92fb74960510b4e56833a))
+- add notificationSubscriptions icon ([#505](https://github.com/mParticle/aquarium/issues/505)) ([b4976bc](https://github.com/mParticle/aquarium/commit/b4976bca3878813fadb453ff2426154b7fa1ef04))
+- utilize generics in UserPreferencesService ([#504](https://github.com/mParticle/aquarium/issues/504)) ([4838a86](https://github.com/mParticle/aquarium/commit/4838a86929bd743f606688ceefeaf13ebf5b1f9d))
+
# [1.35.0](https://github.com/mParticle/aquarium/compare/v1.34.0...v1.35.0) (2024-11-15)
### Bug Fixes
diff --git a/package-lock.json b/package-lock.json
index bf9a97599..05b33f1d7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@mparticle/aquarium",
- "version": "1.35.0",
+ "version": "1.36.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@mparticle/aquarium",
- "version": "1.35.0",
+ "version": "1.36.1",
"license": "Apache-2.0",
"dependencies": {
"lodash.clonedeep": "4.5.0"
@@ -33,6 +33,7 @@
"@types/multer": "1.4.12",
"@typescript-eslint/eslint-plugin": "6.19.0",
"@vitejs/plugin-react": "4.2.1",
+ "@vitest/coverage-v8": "1.3.1",
"concurrently": "8.2.2",
"cz-conventional-changelog": "3.3.0",
"eslint": "8.56.0",
@@ -414,18 +415,18 @@
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz",
- "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+ "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
- "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+ "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
"dev": true,
"engines": {
"node": ">=6.9.0"
@@ -540,10 +541,13 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz",
- "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==",
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz",
+ "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==",
"dev": true,
+ "dependencies": {
+ "@babel/types": "^7.26.0"
+ },
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -805,14 +809,13 @@
}
},
"node_modules/@babel/types": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz",
- "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==",
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz",
+ "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==",
"dev": true,
"dependencies": {
- "@babel/helper-string-parser": "^7.24.7",
- "@babel/helper-validator-identifier": "^7.24.7",
- "to-fast-properties": "^2.0.0"
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
@@ -7146,6 +7149,33 @@
"vite": "^4.2.0 || ^5.0.0"
}
},
+ "node_modules/@vitest/coverage-v8": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.3.1.tgz",
+ "integrity": "sha512-UuBnkSJUNE9rdHjDCPyJ4fYuMkoMtnghes1XohYa4At0MS3OQSAo97FrbwSLRshYsXThMZy1+ybD/byK5llyIg==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.1",
+ "@bcoe/v8-coverage": "^0.2.3",
+ "debug": "^4.3.4",
+ "istanbul-lib-coverage": "^3.2.2",
+ "istanbul-lib-report": "^3.0.1",
+ "istanbul-lib-source-maps": "^4.0.1",
+ "istanbul-reports": "^3.1.6",
+ "magic-string": "^0.30.5",
+ "magicast": "^0.3.3",
+ "picocolors": "^1.0.0",
+ "std-env": "^3.5.0",
+ "test-exclude": "^6.0.0",
+ "v8-to-istanbul": "^9.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "vitest": "1.3.1"
+ }
+ },
"node_modules/@vitest/expect": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.3.1.tgz",
@@ -17486,6 +17516,17 @@
"node": ">=12"
}
},
+ "node_modules/magicast": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz",
+ "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.25.4",
+ "@babel/types": "^7.25.4",
+ "source-map-js": "^1.2.0"
+ }
+ },
"node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -31820,15 +31861,6 @@
"integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
"dev": true
},
- "node_modules/to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
diff --git a/package.json b/package.json
index aa06a8809..75bb14915 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@mparticle/aquarium",
- "version": "1.35.0",
+ "version": "1.36.1",
"description": "mParticle Component Library",
"license": "Apache-2.0",
"keywords": [
@@ -52,6 +52,7 @@
"@types/multer": "1.4.12",
"@typescript-eslint/eslint-plugin": "6.19.0",
"@vitejs/plugin-react": "4.2.1",
+ "@vitest/coverage-v8": "1.3.1",
"concurrently": "8.2.2",
"cz-conventional-changelog": "3.3.0",
"eslint": "8.56.0",
diff --git a/src/assets/svg/crosshair.svg b/src/assets/svg/crosshair.svg
new file mode 100644
index 000000000..dd26ccdb1
--- /dev/null
+++ b/src/assets/svg/crosshair.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/src/assets/svg/mp_pm_lt_notification_subscribe.svg b/src/assets/svg/mp_pm_lt_notification_subscribe.svg
new file mode 100644
index 000000000..94aacc50b
--- /dev/null
+++ b/src/assets/svg/mp_pm_lt_notification_subscribe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/svg/mp_pm_lt_notification_subscribed.svg b/src/assets/svg/mp_pm_lt_notification_subscribed.svg
new file mode 100644
index 000000000..8ae0f1e2c
--- /dev/null
+++ b/src/assets/svg/mp_pm_lt_notification_subscribed.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/src/components/general/Typography/Link.stories.tsx b/src/components/general/Typography/Link.stories.tsx
index 74e9e65bf..8786cd167 100644
--- a/src/components/general/Typography/Link.stories.tsx
+++ b/src/components/general/Typography/Link.stories.tsx
@@ -1,35 +1,74 @@
-import { type Meta } from '@storybook/react'
-import { type StoryObj } from '@storybook/react'
-import { Space } from 'src/components'
+import type { Meta } from '@storybook/react'
+import type { StoryObj } from '@storybook/react'
+import { Flex, Icon, Space, Tooltip } from 'src/components'
import { Typography } from 'src/components/general/Typography/Typography'
import { ExampleStory } from 'src/utils/ExampleStory'
+import { TypographyColors } from './colors'
const meta: Meta = {
- title: 'Components/General/Link',
+ title: 'Components/General/Typography/Link',
component: props => Example Link,
args: {
+ children: 'Example Text',
+ type: undefined,
+ color: undefined,
+ size: 'base',
code: false,
copyable: false,
delete: false,
disabled: false,
editable: false,
ellipsis: false,
- keyboard: false,
mark: false,
strong: false,
italic: false,
- type: undefined,
underline: false,
- onClick: (event: React.MouseEvent) => {
- console.log('Link Clicked')
- },
},
argTypes: {
+ children: {
+ control: 'text',
+ name: 'text',
+ },
type: {
control: 'select',
options: ['secondary', 'success', 'warning', 'danger'],
},
+ size: {
+ control: 'select',
+ options: ['base', 'sm', 'lg', 'xl'],
+ },
+ color: {
+ control: 'select',
+ options: TypographyColors,
+ },
+ copyable: {
+ control: 'boolean',
+ },
+ delete: {
+ control: 'boolean',
+ },
+ disabled: {
+ control: 'boolean',
+ },
+ editable: {
+ control: 'boolean',
+ },
+ ellipsis: {
+ control: 'boolean',
+ },
+ mark: {
+ control: 'boolean',
+ },
+ strong: {
+ control: 'boolean',
+ },
+ italic: {
+ control: 'boolean',
+ },
+ code: {
+ control: 'boolean',
+ },
},
}
export default meta
@@ -43,95 +82,28 @@ type Story = StoryObj
export const Primary: Story = {}
-export const Code: Story = {
- args: {
- code: true,
- },
-}
-
-export const Copyable: Story = {
- args: {
- copyable: true,
- },
-}
-
-export const Deleted: Story = {
- args: {
- delete: true,
- },
-}
-
-export const Disabled: Story = {
- args: {
- disabled: true,
- },
-}
-
-export const Editable: Story = {
- args: {
- editable: true,
- },
-}
-
-export const Keyboard: Story = {
- args: {
- keyboard: true,
- },
-}
-
-export const Marked: Story = {
- args: {
- mark: true,
- },
-}
-
-export const Strong: Story = {
- args: {
- strong: true,
- },
-}
-
-export const Italic: Story = {
- args: {
- italic: true,
- },
-}
-
-export const Success: Story = {
- args: {
- type: 'success',
- },
-}
-
-export const Secondary: Story = {
- args: {
- type: 'secondary',
- },
-}
-
-export const Warning: Story = {
- args: {
- type: 'warning',
- },
-}
-
-export const Danger: Story = {
- args: {
- type: 'danger',
- },
-}
-
-export const Underline: Story = {
- args: {
- underline: true,
- },
-}
-
-export const CustomOnClick: Story = {
- args: {
- onClick: event => {
- alert('Custom Click Handler')
- },
+export const InsideTooltip: Story = {
+ render: () => {
+ return (
+
+
+ Typography inside of a tooltip, hover icon to see
+
+
+ Help lorem ipsum{' '}
+
+ Learn More
+
+
+ >
+ }>
+
+
+
+
+ )
},
}
diff --git a/src/components/general/Typography/Paragraph.stories.tsx b/src/components/general/Typography/Paragraph.stories.tsx
index e77bb86c7..1719a6473 100644
--- a/src/components/general/Typography/Paragraph.stories.tsx
+++ b/src/components/general/Typography/Paragraph.stories.tsx
@@ -1,5 +1,5 @@
-import { type Meta } from '@storybook/react'
-import { type StoryObj } from '@storybook/react'
+import type { Meta } from '@storybook/react'
+import type { StoryObj } from '@storybook/react'
import { Typography } from 'src/components/general/Typography/Typography'
import { ExampleStory } from 'src/utils/ExampleStory'
import { useMemo } from 'react'
@@ -8,12 +8,16 @@ import { Radio } from 'src/components'
import { Switch } from 'src/components'
import { Slider } from 'src/components'
import { Icon } from 'src/components'
+import { TypographyColors } from './colors'
const meta: Meta = {
- title: 'Components/General/Typography.Paragraph',
+ title: 'Components/General/Typography/Paragraph',
component: props => Paragraph text goes here,
-
args: {
+ children: 'Example Text',
+ type: undefined,
+ color: undefined,
+ size: 'base',
code: false,
copyable: false,
delete: false,
@@ -23,17 +27,52 @@ const meta: Meta = {
mark: false,
strong: false,
italic: false,
- type: undefined,
underline: false,
- onClick: event => {
- console.log('Paragraph Clicked')
- },
},
argTypes: {
+ children: {
+ control: 'text',
+ name: 'text',
+ },
type: {
control: 'select',
options: ['secondary', 'success', 'warning', 'danger'],
},
+ size: {
+ control: 'select',
+ options: ['base', 'sm', 'lg', 'xl'],
+ },
+ color: {
+ control: 'select',
+ options: TypographyColors,
+ },
+ copyable: {
+ control: 'boolean',
+ },
+ delete: {
+ control: 'boolean',
+ },
+ disabled: {
+ control: 'boolean',
+ },
+ editable: {
+ control: 'boolean',
+ },
+ ellipsis: {
+ control: 'boolean',
+ },
+ mark: {
+ control: 'boolean',
+ },
+ strong: {
+ control: 'boolean',
+ },
+ italic: {
+ control: 'boolean',
+ },
+ code: {
+ control: 'boolean',
+ },
},
}
export default meta
@@ -47,98 +86,6 @@ type Story = StoryObj
export const Primary: Story = {}
-export const Code: Story = {
- args: {
- code: true,
- },
-}
-
-export const Copyable: Story = {
- args: {
- copyable: true,
- },
-}
-
-export const DeletedLine: Story = {
- args: {
- delete: true,
- },
-}
-
-export const Disabled: Story = {
- args: {
- disabled: true,
- },
-}
-
-export const Editable: Story = {
- args: {
- editable: true,
- },
-}
-
-export const Ellipsis: Story = {
- args: {
- ellipsis: true,
- },
-}
-
-export const Marked: Story = {
- args: {
- mark: true,
- },
-}
-
-export const Strong: Story = {
- args: {
- strong: true,
- },
-}
-
-export const Italic: Story = {
- args: {
- italic: true,
- },
-}
-
-export const Success: Story = {
- args: {
- type: 'success',
- },
-}
-
-export const Secondary: Story = {
- args: {
- type: 'secondary',
- },
-}
-
-export const Warning: Story = {
- args: {
- type: 'warning',
- },
-}
-
-export const Danger: Story = {
- args: {
- type: 'danger',
- },
-}
-
-export const Underline: Story = {
- args: {
- underline: true,
- },
-}
-
-export const CustomOnClick: Story = {
- args: {
- onClick: event => {
- alert('Custom Click Handler')
- },
- },
-}
-
export const ExampleEditable: Story = {
render: () => {
const [editableStr, setEditableStr] = useState('This is an editable text.')
diff --git a/src/components/general/Typography/Text.stories.tsx b/src/components/general/Typography/Text.stories.tsx
index 35a867493..3545ff394 100644
--- a/src/components/general/Typography/Text.stories.tsx
+++ b/src/components/general/Typography/Text.stories.tsx
@@ -1,35 +1,37 @@
-import { type Meta } from '@storybook/react'
-import { type StoryObj } from '@storybook/react'
+import type { Meta } from '@storybook/react'
+import type { StoryObj } from '@storybook/react'
import { Space } from 'src/components'
import { Switch } from 'src/components'
import { Typography } from 'src/components/general/Typography/Typography'
import { ExampleStory } from 'src/utils/ExampleStory'
import { useState } from 'react'
import { expect } from '@storybook/test'
+import { TypographyColors } from './colors'
const meta: Meta = {
- title: 'Components/General/Typography.Text',
- component: props => Example Text,
-
+ title: 'Components/General/Typography/Text',
+ component: props => {props.children},
args: {
- code: false,
+ children: 'Example Text',
+ type: undefined,
+ color: undefined,
size: 'base',
+ code: false,
copyable: false,
delete: false,
disabled: false,
editable: false,
ellipsis: false,
- keyboard: false,
mark: false,
strong: false,
italic: false,
- type: undefined,
underline: false,
- onClick: (event: React.MouseEvent) => {
- console.log('Text Clicked')
- },
},
argTypes: {
+ children: {
+ control: 'text',
+ name: 'text',
+ },
type: {
control: 'select',
options: ['secondary', 'success', 'warning', 'danger'],
@@ -38,17 +40,43 @@ const meta: Meta = {
control: 'select',
options: ['base', 'sm', 'lg', 'xl'],
},
+ color: {
+ control: 'select',
+ options: TypographyColors,
+ },
+ copyable: {
+ control: 'boolean',
+ },
+ delete: {
+ control: 'boolean',
+ },
+ disabled: {
+ control: 'boolean',
+ },
+ editable: {
+ control: 'boolean',
+ },
+ ellipsis: {
+ control: 'boolean',
+ },
+ mark: {
+ control: 'boolean',
+ },
+ strong: {
+ control: 'boolean',
+ },
+ italic: {
+ control: 'boolean',
+ },
+ code: {
+ control: 'boolean',
+ },
},
}
export default meta
type Story = StoryObj
-/*
- Initial story templates generated by AI.
- Customize the stories based on specific requirements.
-*/
-
export const Primary: Story = {
play: async context => {
const text = context.canvasElement.querySelector('span')
@@ -56,116 +84,24 @@ export const Primary: Story = {
},
}
-export const Code: Story = {
- args: {
- code: true,
- },
-}
-
-export const Copyable: Story = {
- args: {
- copyable: true,
- },
-}
-
-export const Deleted: Story = {
- args: {
- delete: true,
- },
-}
-
-export const Disabled: Story = {
- args: {
- disabled: true,
- },
-}
-
-export const Editable: Story = {
- args: {
- editable: true,
- },
-}
-
-export const Keyboard: Story = {
- args: {
- keyboard: true,
- },
-}
-
-export const Marked: Story = {
- args: {
- mark: true,
- },
-}
-
-export const Strong: Story = {
- args: {
- strong: true,
- },
-}
-
-export const Italic: Story = {
- args: {
- italic: true,
- },
-}
-
-export const Success: Story = {
- args: {
- type: 'success',
- },
-}
-
-export const Secondary: Story = {
- args: {
- type: 'secondary',
- },
-}
-
-export const Warning: Story = {
- args: {
- type: 'warning',
- },
-}
-
-export const Danger: Story = {
- args: {
- type: 'danger',
- },
-}
-
-export const Underline: Story = {
- args: {
- underline: true,
- },
-}
-
-export const CustomOnClick: Story = {
- args: {
- onClick: event => {
- alert('Custom Click Handler')
- },
- },
-}
-
export const ExampleTexts: Story = {
render: () => {
return (
>}>
- Ant Design (default)
- Ant Design (secondary)
- Ant Design (success)
- Ant Design (warning)
- Ant Design (danger)
- Ant Design (disabled)
- Ant Design (mark)
- Ant Design (code)
- Ant Design (keyboard)
- Ant Design (underline)
- Ant Design (delete)
- Ant Design (strong)
- Ant Design (italic)
+ Aquarium (default)
+ Aquarium (secondary)
+ Aquarium (success)
+ Aquarium (warning)
+ Aquarium (danger)
+ Aquarium (disabled)
+ Aquarium (mark)
+ Aquarium (code)
+ Aquarium (keyboard)
+ Aquarium (underline)
+ Aquarium (delete)
+ Aquarium (strong)
+ Aquarium (italic)
)
diff --git a/src/components/general/Typography/Title.stories.tsx b/src/components/general/Typography/Title.stories.tsx
index 3d60f9b03..61617f1f4 100644
--- a/src/components/general/Typography/Title.stories.tsx
+++ b/src/components/general/Typography/Title.stories.tsx
@@ -1,38 +1,35 @@
-import { type Meta } from '@storybook/react'
-import { type StoryObj } from '@storybook/react'
+import type { Meta } from '@storybook/react'
+import type { StoryObj } from '@storybook/react'
import { Typography } from 'src/components/general/Typography/Typography'
import { ExampleStory } from 'src/utils/ExampleStory'
const meta: Meta = {
- title: 'Components/General/Title',
+ title: 'Components/General/Typography/Title',
component: Typography.Title,
-
- args: {},
}
+
export default meta
type Story = StoryObj
-export const Primary = {}
-
-export const ExampleHeadings: Story = {
+export const Primary: Story = {
render: () => {
return (
- h1. Ant Design
+ h1. Aquarium Component Library
- h2. Ant Design
+ h2. Aquarium Component Library
- h3. Ant Design
+ h3. Aquarium Component Library
- h4. Ant Design
+ h4. Aquarium Component Library
- h5. Ant Design
+ h5. Aquarium Component Library
)
diff --git a/src/components/general/Typography/Typography.tsx b/src/components/general/Typography/Typography.tsx
index 76a911c92..2538ced80 100644
--- a/src/components/general/Typography/Typography.tsx
+++ b/src/components/general/Typography/Typography.tsx
@@ -1,29 +1,14 @@
-import {
- Typography as AntTypography,
- type TypographyProps as AntTypographyProps,
- ConfigProvider as AntConfigProvider,
-} from 'antd'
+import { Typography as AntTypography } from 'antd'
import { ConfigProvider } from 'src/components'
-import { type ReactNode } from 'react'
-import { type TextProps as AntTextProps } from 'antd/es/typography/Text'
-import { type TitleProps as AntTitleProps } from 'antd/es/typography/Title'
-import { type LinkProps as AntLinkProps } from 'antd/es/typography/Link'
-import { type ParagraphProps as AntParagraphProps } from 'antd/es/typography/Paragraph'
+import type { ReactNode } from 'react'
+import type { TextProps as AntTextProps } from 'antd/es/typography/Text'
+import type { TitleProps as AntTitleProps } from 'antd/es/typography/Title'
+import type { LinkProps as AntLinkProps } from 'antd/es/typography/Link'
+import type { ParagraphProps as AntParagraphProps } from 'antd/es/typography/Paragraph'
+import { getColorFromStyles, type TypographyColor } from './colors'
+import { ColorTextLightSolid } from 'src/styles/style'
-export interface ITypographyProps extends AntTypographyProps {
- children: ReactNode
-}
-
-export const Typography = (props: ITypographyProps) => (
-
- {props.children}
-
-)
-
-type TypographySize = 'base' | 'sm' | 'lg' | 'xl'
-export interface ITextProps extends AntTextProps {
- size?: TypographySize
-}
+type TypographySize = 'base' | 'sm' | 'lg' | 'xl' | number
// TODO: Replace hardcoded values in getFontSize and getLineHeight with tokens when design is ready
// These values are currently coming from https://www.figma.com/design/LffDbOUjeYqDMZ3djs9Cga/mParticle-Foundation-v1.0.1?node-id=3745-8164&m=dev
@@ -31,7 +16,8 @@ const getFontSize = (size: TypographySize): number => {
if (size === 'base') return 14
if (size === 'sm') return 12
if (size === 'lg') return 16
- return 20
+ if (size === 'xl') return 20
+ return size
}
const getLineHeight = (size: TypographySize): number => {
@@ -41,50 +27,106 @@ const getLineHeight = (size: TypographySize): number => {
return 1.4
}
-const Text = ({ size = 'base', ...props }: ITextProps) => {
- const fontSize = getFontSize(size)
- const lineHeight = getLineHeight(size)
+interface InternalTypographyProps {
+ size?: TypographySize
+ color?: TypographyColor
+ children: ReactNode
+}
+
+export const Typography = () => {
+ return <>DO NOT USE>
+}
+
+type InternalTextProps = InternalTypographyProps & AntTextProps
+type InternalTitleProps = InternalTypographyProps & AntTitleProps
+type InternalLinkProps = InternalTypographyProps & AntLinkProps
+type InternalParagraphProps = InternalTypographyProps & AntParagraphProps
+
+export interface ITextProps extends InternalTextProps {
+ tooltip?: boolean
+}
+
+export interface ITitleProps extends InternalTitleProps {}
+
+export interface ILinkProps extends InternalLinkProps {
+ tooltip?: boolean
+}
+
+export interface IParagraphProps extends InternalParagraphProps {}
+
+// Tried generalizing into a higher order component but couldn't do it type-safely, so just repeated the code
+const Text = (props: ITextProps) => {
+ const { size, color, type, tooltip, children, ...rest } = props
+
+ const fontSize = size ? getFontSize(size) : undefined
+ const lineHeight = size ? getLineHeight(size) : undefined
+ const fontColor = !type && color ? getColorFromStyles(color) : tooltip ? ColorTextLightSolid : undefined
return (
-
- {props.children}
-
+
+ {children}
+
)
}
Typography.Text = Text
-interface ITitleProps extends AntTitleProps {
- children: ReactNode
+const Title = (props: ITitleProps) => {
+ const { size, color, type, children, ...rest } = props
+
+ const fontSize = size ? getFontSize(size) : undefined
+ const lineHeight = size ? getLineHeight(size) : undefined
+ const fontColor = !type && color ? getColorFromStyles(color) : undefined
+
+ return (
+
+
+ {children}
+
+
+ )
}
-const Title = (props: ITitleProps) => (
-
- {props.children}
-
-)
Typography.Title = Title
-export interface ILinkProps extends AntLinkProps {
- children: ReactNode
+const Link = (props: ILinkProps) => {
+ const { size, color, type, tooltip, underline, children, ...rest } = props
+
+ const fontSize = size ? getFontSize(size) : undefined
+ const lineHeight = size ? getLineHeight(size) : undefined
+ const fontColor = !type && color ? getColorFromStyles(color) : tooltip ? ColorTextLightSolid : undefined
+
+ return (
+
+
+ {children}
+
+
+ )
}
-const Link = (props: ILinkProps) => (
-
- {props.children}
-
-)
Typography.Link = Link
-export interface IParagraphProps extends AntParagraphProps {
- children: ReactNode
+const Paragraph = (props: IParagraphProps) => {
+ const { size, color, type, children, ...rest } = props
+
+ const fontSize = size ? getFontSize(size) : undefined
+ const lineHeight = size ? getLineHeight(size) : undefined
+ const fontColor = !type && color ? getColorFromStyles(color) : undefined
+
+ return (
+
+
+ {children}
+
+
+ )
}
-const Paragraph = (props: IParagraphProps) => (
-
- {props.children}
-
-)
Typography.Paragraph = Paragraph
diff --git a/src/components/general/Typography/colors.ts b/src/components/general/Typography/colors.ts
new file mode 100644
index 000000000..af807bcd0
--- /dev/null
+++ b/src/components/general/Typography/colors.ts
@@ -0,0 +1,41 @@
+import * as styles from '../../../styles/style'
+
+export const TypographyColors = [
+ 'ColorText',
+ 'ColorTextSecondary',
+ 'ColorTextTertiary',
+ 'ColorTextQuaternary',
+ 'ColorInfoText',
+ 'ColorInfoTextActive',
+ 'ColorPrimaryTextHover',
+ 'ColorPrimaryText',
+ 'ColorPrimaryTextActive',
+ 'ColorSuccessTextHover',
+ 'ColorSuccessText',
+ 'ColorSuccessTextActive',
+ 'ColorErrorTextHover',
+ 'ColorErrorText',
+ 'ColorErrorTextActive',
+ 'ColorWarningTextHover',
+ 'ColorWarningText',
+ 'ColorWarningTextActive',
+ 'ColorLink',
+ 'ColorLinkHover',
+ 'ColorLinkActive',
+ 'ColorTextPlaceholder',
+ 'ColorTextDisabled',
+ 'ColorTextHeading',
+ 'ColorTextLabel',
+ 'ColorTextDescription',
+ 'ColorTextLightSolid',
+] as const
+
+export type TypographyColor = (typeof TypographyColors)[number]
+
+export function getColorFromStyles(color: TypographyColor | string): string {
+ if (styles[color as TypographyColor]) {
+ return (styles as unknown as Record)[color as TypographyColor]
+ }
+
+ return color
+}
diff --git a/src/components/icons/index.ts b/src/components/icons/index.ts
index 289ee159f..9fddb3642 100644
--- a/src/components/icons/index.ts
+++ b/src/components/icons/index.ts
@@ -8,6 +8,7 @@ import ChartLineIcon from 'src/assets/svg/chart-line.svg?react'
import CheckIcon from 'src/assets/svg/check.svg?react'
import CircleNodesIcon from 'src/assets/svg/circle-nodes.svg?react'
import CloudIcon from 'src/assets/svg/cloud.svg?react'
+import CrosshairIcon from 'src/assets/svg/crosshair.svg?react'
import ConnectionsIcon from 'src/assets/svg/connections.svg?react'
import DataPlatformIconDt from 'src/assets/svg/mp_pm_dt_data-platform.svg?react'
import DatabaseIcon from 'src/assets/svg/database.svg?react'
@@ -60,6 +61,8 @@ import DirectoryIcon from 'src/assets/svg/mp_pm_lt_directory.svg?react'
import LockIcon from 'src/assets/svg/mp_act_lt_lock.svg?react'
import UnlockIcon from 'src/assets/svg/mp_act_lt_unlock.svg?react'
import NotificationIcon from 'src/assets/svg/mp_pm_lt_notification.svg?react'
+import NotificationSubscribedIcon from 'src/assets/svg/mp_pm_lt_notification_subscribed.svg?react'
+import NotificationSubscribeIcon from 'src/assets/svg/mp_pm_lt_notification_subscribe.svg?react'
import PremiumIconDt from 'src/assets/svg/mp_info_dt_premium.svg?react'
import OverviewIconDt from 'src/assets/svg/mp_pm_dt_overview.svg?react'
import FilterIcon from 'src/assets/svg/mp_act_lt_filter.svg?react'
@@ -104,6 +107,7 @@ export {
CheckIcon,
CircleNodesIcon,
CloudIcon,
+ CrosshairIcon,
Cohort,
ConnectionsIcon,
ConversionIcon,
@@ -146,6 +150,8 @@ export {
MpLogoIcon,
NextIcon,
NotificationIcon,
+ NotificationSubscribedIcon,
+ NotificationSubscribeIcon,
ObservabilityIcon,
OpenTabIcon,
Organization,
diff --git a/src/components/index.ts b/src/components/index.ts
index d33abfaa8..60b1a6f4d 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -121,19 +121,17 @@ export { SuiteLogo } from './navigation/GlobalNavigation/SuiteLogo'
export { Typography } from './general/Typography/Typography'
// UPS
-export { UserPreferencesService } from '../services/user-preferences/user-preferences'
+export { UserPreferencesService } from '../services/user-preferences'
export { CompositeUserPreferencesService } from '../services/user-preferences/composite-user-preferences-service'
-export { type CompositeUserPreferences } from '../services/user-preferences/models/user-preferences/composite-user-preferences'
export {
UserPreferenceScopeType,
type UserPreferenceDefinition,
type UserPreferenceDefinitions,
+ type UserPreferencesPerScope,
} from '../services/user-preferences/models/definitions'
export {
- type UserPreferences,
USER_PREFERENCE_SCOPE_SEPARATOR,
UserPreferenceGlobalScope,
- type UserPreference,
type UserPreferenceScope,
} from '../services/user-preferences/models/storage-models'
export {
diff --git a/src/constants/Icons.ts b/src/constants/Icons.ts
index e392cdff2..dae4f0609 100644
--- a/src/constants/Icons.ts
+++ b/src/constants/Icons.ts
@@ -11,6 +11,7 @@ import {
CheckIcon,
CircleNodesIcon,
CloudIcon,
+ CrosshairIcon,
ConnectionsIcon,
DataPlatformIconDt,
DatabaseIcon,
@@ -63,6 +64,8 @@ import {
LockIcon,
UnlockIcon,
NotificationIcon,
+ NotificationSubscribedIcon,
+ NotificationSubscribeIcon,
PremiumIconDt,
OverviewIconDt,
FilterIcon,
@@ -184,6 +187,10 @@ export const Icons: Record = {
light: Copy,
default: 'light',
},
+ crosshair: {
+ light: CrosshairIcon,
+ default: 'light',
+ },
dashboard: {
light: Dashboard,
default: 'light',
@@ -362,6 +369,14 @@ export const Icons: Record = {
light: NotificationIcon,
default: 'light',
},
+ notificationSubscribed: {
+ light: NotificationSubscribedIcon,
+ default: 'light',
+ },
+ notificationSubscribe: {
+ light: NotificationSubscribeIcon,
+ default: 'light',
+ },
observability: {
light: ObservabilityIcon,
default: 'light',
diff --git a/src/services/user-preferences/composite-user-preferences-service/composite-user-preferences-service.spec.ts b/src/services/user-preferences/composite-user-preferences-service/composite-user-preferences-service.spec.ts
index 0aece0f1e..84603249e 100644
--- a/src/services/user-preferences/composite-user-preferences-service/composite-user-preferences-service.spec.ts
+++ b/src/services/user-preferences/composite-user-preferences-service/composite-user-preferences-service.spec.ts
@@ -1,31 +1,31 @@
import { describe, it, expect, beforeEach } from 'vitest'
import { CompositeUserPreferencesService } from 'src/services/user-preferences/composite-user-preferences-service'
-import { type UserPreferences } from 'src/services/user-preferences/models/storage-models/user-preferences'
import { type UserPreferenceScope } from 'src/services/user-preferences/models/storage-models/user-preference-scope'
-import { type UserPreferenceDefinitions } from 'src/services/user-preferences/models/definitions/user-preference-definitions'
import { UserPreferenceScopeType } from 'src/services/user-preferences/models/definitions/user-preference-scope-type'
import { type UserPreferenceDefinition } from 'src/services/user-preferences/models/definitions/user-preference-definition'
import {
makeBuilderFromDefinition,
+ type TestType,
TestUserPreferenceDefinitionsFakeFactory,
- TestUserPreferenceId,
type TestUserPreferencesFakeBuilder,
TestUserPreferencesFakeFactory,
} from 'src/services/user-preferences/user-preferences-service.spec'
+import { type UserPreferenceDefinitions } from 'src/components'
+import { type UserPreferencesPerScope } from '../models/definitions/user-preference-per-scope'
describe('When testing CompositeUserPreferencesService', () => {
- let compositeUserPreferencesService: CompositeUserPreferencesService
- let userPreferences: UserPreferences
+ let compositeUserPreferencesService: CompositeUserPreferencesService
+ let userPreferences: UserPreferencesPerScope
let expectedScope: UserPreferenceScope
- let definitions: UserPreferenceDefinitions
+ let definitions: UserPreferenceDefinitions
beforeEach(() => {
- definitions = TestUserPreferenceDefinitionsFakeFactory() as UserPreferenceDefinitions
+ definitions = TestUserPreferenceDefinitionsFakeFactory() as UserPreferenceDefinitions
const prefsBuilder = makeBuilderFromDefinition(definitions)
- userPreferences = TestUserPreferencesFakeFactory([prefsBuilder]) as UserPreferences
+ userPreferences = TestUserPreferencesFakeFactory([prefsBuilder]) as UserPreferencesPerScope
expectedScope = Object.keys(userPreferences)[0] as UserPreferenceScope
- compositeUserPreferencesService = new CompositeUserPreferencesService()
+ compositeUserPreferencesService = new CompositeUserPreferencesService()
})
describe('and getting scoped user preferences', () => {
@@ -42,8 +42,8 @@ describe('When testing CompositeUserPreferencesService', () => {
// assert
Object.entries(actualScopedUserPreferences).forEach(([preferenceId, actualPreference]) => {
- const definition = definitions[preferenceId as TestUserPreferenceId]
- const expectedScopedUserPreferences = { optedIn: definition?.isOptedInByDefault }
+ const definition = definitions[preferenceId as keyof TestType]
+ const expectedScopedUserPreferences = definition.defaultValue
expect(actualPreference).toEqual(expectedScopedUserPreferences)
})
})
@@ -77,21 +77,26 @@ describe('When testing CompositeUserPreferencesService', () => {
allowedScope: UserPreferenceScopeType,
) => {
// arrange
+ const { preferenceId: builderPreferenceId } = getFirstDefinition(definitions)
+ const updatingId = builderPreferenceId
+
const userPreferencesBuilder: TestUserPreferencesFakeBuilder[] = [
- { scope: expectedScope, userPreferenceIds: [TestUserPreferenceId.Default], optedIns: [true] },
+ {
+ scope: expectedScope,
+ keys: [updatingId],
+ defaultValues: [{ isOptedIn: true }],
+ },
{ wantsRandom: true },
{ wantsRandom: true },
]
- userPreferences = TestUserPreferencesFakeFactory(
- userPreferencesBuilder,
- ) as UserPreferences
+ userPreferences = TestUserPreferencesFakeFactory(userPreferencesBuilder) as UserPreferencesPerScope
definitions = TestUserPreferenceDefinitionsFakeFactory([
{
- id: TestUserPreferenceId.Default,
+ id: 'Default',
isOptedInByDefault: true,
allowedScope,
},
- ]) as UserPreferenceDefinitions
+ ]) as UserPreferenceDefinitions
// act
const actualScopedUserPreferences = compositeUserPreferencesService.getScopedUserPreferences(
@@ -124,24 +129,26 @@ describe('When testing CompositeUserPreferencesService', () => {
allowedScope: UserPreferenceScopeType,
) => {
// arrange
- const testPreferenceValue = true
+ const testPreferenceValues = { isOptedIn: true }
const { preferenceId: builderPreferenceId } = getFirstDefinition(definitions)
const updatingId = builderPreferenceId
const userPreferencesBuilder: TestUserPreferencesFakeBuilder[] = [
- { scope: expectedScope, userPreferenceIds: [updatingId], optedIns: [testPreferenceValue] },
+ {
+ scope: expectedScope,
+ keys: [updatingId],
+ defaultValues: [testPreferenceValues],
+ },
{ wantsRandom: true },
{ wantsRandom: true },
]
- userPreferences = TestUserPreferencesFakeFactory(
- userPreferencesBuilder,
- ) as UserPreferences
+ userPreferences = TestUserPreferencesFakeFactory(userPreferencesBuilder) as UserPreferencesPerScope
// act
- const expectedPreferenceValue = !testPreferenceValue
+ const expectedValues = { isOptedIn: false }
const actualUserPreferences = compositeUserPreferencesService.getUpdatedUserPreferenceStorageObject(
updatingId,
- expectedPreferenceValue,
+ expectedValues,
currentScope,
userPreferences,
allowedScope,
@@ -149,7 +156,7 @@ describe('When testing CompositeUserPreferencesService', () => {
// assert
const actualUserPreference = actualUserPreferences?.[expectedScope]?.[updatingId]
- expect(actualUserPreference?.optedIn).toEqual(expectedPreferenceValue)
+ expect(actualUserPreference?.isOptedIn).toEqual(expectedValues.isOptedIn)
expect(actualUserPreference).not.toBe(userPreferences)
},
)
@@ -169,15 +176,16 @@ describe('When testing CompositeUserPreferencesService', () => {
allowedScope: UserPreferenceScopeType,
) => {
// arrange
- const expectedPreferenceValue = true
- const updatingId = TestUserPreferenceId.Default
+ const expectedValues = { isOptedIn: true }
+ const { preferenceId: builderPreferenceId } = getFirstDefinition(definitions)
+ const updatingId = builderPreferenceId
- userPreferences = {} satisfies UserPreferences
+ userPreferences = {} satisfies UserPreferencesPerScope
// act
const actualUserPreferences = compositeUserPreferencesService.getUpdatedUserPreferenceStorageObject(
updatingId,
- expectedPreferenceValue,
+ expectedValues,
expectedScope,
userPreferences,
allowedScope,
@@ -185,19 +193,19 @@ describe('When testing CompositeUserPreferencesService', () => {
// assert
const actualUserPreference = actualUserPreferences?.[expectedScope]?.[updatingId]
- expect(actualUserPreference?.optedIn).toEqual(expectedPreferenceValue)
+ expect(actualUserPreference?.isOptedIn).toEqual(expectedValues.isOptedIn)
expect(actualUserPreference).not.toBe(userPreferences)
},
)
})
})
-function getFirstDefinition(definitions: UserPreferenceDefinitions): {
- definition?: UserPreferenceDefinition
- preferenceId: TestUserPreferenceId
+function getFirstDefinition(definitions: UserPreferenceDefinitions): {
+ definition: UserPreferenceDefinition
+ preferenceId: keyof TestType
} {
- const preferenceId = Object.keys(definitions)[0] as TestUserPreferenceId
- const definition = definitions[preferenceId]
+ const preferenceId = Object.keys(definitions)[0] as keyof TestType
+ const definition = definitions[preferenceId] as unknown as UserPreferenceDefinition
return { definition, preferenceId }
}
diff --git a/src/services/user-preferences/composite-user-preferences-service/composite-user-preferences-service.ts b/src/services/user-preferences/composite-user-preferences-service/composite-user-preferences-service.ts
index f0a26eb03..edcdff3bd 100644
--- a/src/services/user-preferences/composite-user-preferences-service/composite-user-preferences-service.ts
+++ b/src/services/user-preferences/composite-user-preferences-service/composite-user-preferences-service.ts
@@ -1,78 +1,75 @@
-import { type UserPreferences } from 'src/services/user-preferences/models/storage-models/user-preferences'
+import {
+ type UserPreferenceDefinition,
+ UserPreferenceScopeType,
+ type UserPreferenceDefinitions,
+} from 'src/services/user-preferences/models/definitions'
import {
USER_PREFERENCE_SCOPE_SEPARATOR,
UserPreferenceGlobalScope,
type UserPreferenceScope,
-} from 'src/services/user-preferences/models/storage-models/user-preference-scope'
-import { type UserPreferenceDefinitions } from 'src/services/user-preferences/models/definitions/user-preference-definitions'
-import { type CompositeUserPreferences } from 'src/services/user-preferences/models/user-preferences/composite-user-preferences'
-import { type UserPreference } from 'src/services/user-preferences/models/storage-models/user-preference'
-import { UserPreferenceScopeType } from 'src/services/user-preferences/models/definitions/user-preference-scope-type'
+} from 'src/services/user-preferences/models/storage-models'
import cloneDeep from 'lodash/cloneDeep'
-import { type UserPreferenceDefinition } from 'src/services/user-preferences/models/definitions/user-preference-definition'
+import { type UserPreferencesPerScope } from '../models/definitions/user-preference-per-scope'
-export class CompositeUserPreferencesService {
+export class CompositeUserPreferencesService> {
public getScopedUserPreferences(
- storedPreferences: UserPreferences,
+ userPreferencesPerScope: UserPreferencesPerScope,
currentScope: UserPreferenceScope,
- definitions: UserPreferenceDefinitions,
- ): CompositeUserPreferences {
- const entriesByIdAndUserPreference = Object.entries(definitions).map<[TPreferenceIds, UserPreference]>(
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-expect-error
- this.createUserPreferenceEntryFromDefinition.bind(this, storedPreferences, currentScope),
- )
-
- return this.createCompositePreferencesFromEntries(entriesByIdAndUserPreference)
+ definitions: UserPreferenceDefinitions,
+ ): T {
+ const entriesByIdAndUserPreference = (Object.keys(definitions) as Array).map(key => {
+ const definition = definitions[key]
+ return this.createUserPreferenceEntryFromDefinition(userPreferencesPerScope, currentScope, key, definition)
+ })
+
+ const composite: T = {} as unknown as T
+
+ for (const [key, value] of entriesByIdAndUserPreference) {
+ composite[key] = value
+ }
+
+ return composite
}
- public getUpdatedUserPreferenceStorageObject(
- preferenceId: TPreferenceIds,
- isOptedIn: boolean,
+ public getUpdatedUserPreferenceStorageObject(
+ userPreferenceKey: TKey,
+ value: T[TKey],
currentScope: UserPreferenceScope,
- currentPreferences: UserPreferences,
+ currentPreferences: UserPreferencesPerScope,
allowedScope: UserPreferenceScopeType,
- ): UserPreferences {
- const userPreferencesToUpdate = currentPreferences ? cloneDeep(currentPreferences) : {}
+ ): UserPreferencesPerScope {
+ const userPreferencesToUpdate: UserPreferencesPerScope = currentPreferences ? cloneDeep(currentPreferences) : {}
const effectiveScope = this.getEffectiveScope(currentScope, allowedScope)
- const scopedUserPreferences = userPreferencesToUpdate[effectiveScope] ?? {}
+ const scopedUserPreferences: T = userPreferencesToUpdate[effectiveScope] ?? ({} as unknown as T)
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-expect-error
- scopedUserPreferences[preferenceId] = { optedIn: isOptedIn }
+ scopedUserPreferences[userPreferenceKey] = value
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-expect-error
userPreferencesToUpdate[effectiveScope] = scopedUserPreferences
return userPreferencesToUpdate
}
- private createUserPreferenceEntryFromDefinition(
- storedPreferences: UserPreferences,
+ private createUserPreferenceEntryFromDefinition(
+ userPreferencesPerScope: UserPreferencesPerScope,
currentScope: UserPreferenceScope,
- [definedUserPreferenceId, definition]: [TPreferenceIds, UserPreferenceDefinition],
- ): [TPreferenceIds, UserPreference] {
- if (!storedPreferences) {
- const userPreferenceDefault = { optedIn: definition.isOptedInByDefault }
- return this.createPreferenceEntry(definedUserPreferenceId, userPreferenceDefault)
+ userPreferenceKey: TKey,
+ definition: UserPreferenceDefinition,
+ ): [TKey, TValue] {
+ if (!userPreferencesPerScope) {
+ return [userPreferenceKey, definition.defaultValue]
}
- const { allowedScope } = definition
+ const effectiveScope = this.getEffectiveScope(currentScope, definition.allowedScope)
- const effectiveScope = this.getEffectiveScope(currentScope, allowedScope)
+ const scopedUserPreferences = userPreferencesPerScope[effectiveScope]
- const scopedUserPreferences = storedPreferences[effectiveScope]
+ const valueForCurrentScope = scopedUserPreferences?.[userPreferenceKey]
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-expect-error
- const userPreferenceForCurrentDefinition: UserPreference = scopedUserPreferences?.[definedUserPreferenceId]
+ const value = valueForCurrentScope ?? definition.defaultValue
- const optedIn = userPreferenceForCurrentDefinition?.optedIn ?? definition.isOptedInByDefault
- const userPreference = { optedIn }
- return this.createPreferenceEntry(definedUserPreferenceId, userPreference)
+ return [userPreferenceKey, value as TValue]
}
private getEffectiveScope(
@@ -102,32 +99,6 @@ export class CompositeUserPreferencesService
currenScopeParts.length = scopeLength // Truncating scope parts that are too granular for the allowed scope
- const effectiveScope = currenScopeParts.join(USER_PREFERENCE_SCOPE_SEPARATOR) as UserPreferenceScope
-
- return effectiveScope
- }
-
- private createPreferenceEntry(
- userPreferenceId: TPreferenceIds,
- userPreference: UserPreference,
- ): [TPreferenceIds, UserPreference] {
- return [userPreferenceId, userPreference]
- }
-
- // TODO: Should be replaced with Object.fromEntries when the transpiler is updated
- private createCompositePreferencesFromEntries(
- entries: Array<[TPreferenceIds, UserPreference]>,
- ): CompositeUserPreferences {
- return entries.reduce(
- (
- composite: CompositeUserPreferences,
- [userPreferenceId, preference]: [TPreferenceIds, UserPreference],
- ) => {
- composite[userPreferenceId] = preference
- return composite
- },
- // eslint-disable-next-line
- {} as CompositeUserPreferences,
- )
+ return currenScopeParts.join(USER_PREFERENCE_SCOPE_SEPARATOR) as UserPreferenceScope
}
}
diff --git a/src/services/user-preferences/index.ts b/src/services/user-preferences/index.ts
new file mode 100644
index 000000000..b7931a207
--- /dev/null
+++ b/src/services/user-preferences/index.ts
@@ -0,0 +1 @@
+export * from './user-preferences-service'
diff --git a/src/services/user-preferences/models/definitions/index.ts b/src/services/user-preferences/models/definitions/index.ts
index 1037109ca..2dac49be5 100644
--- a/src/services/user-preferences/models/definitions/index.ts
+++ b/src/services/user-preferences/models/definitions/index.ts
@@ -1,3 +1,4 @@
export * from './user-preference-definition'
export * from './user-preference-definitions'
export * from './user-preference-scope-type'
+export * from './user-preference-per-scope'
diff --git a/src/services/user-preferences/models/definitions/user-preference-definition.ts b/src/services/user-preferences/models/definitions/user-preference-definition.ts
index 22a5d34f8..d7df18c5a 100644
--- a/src/services/user-preferences/models/definitions/user-preference-definition.ts
+++ b/src/services/user-preferences/models/definitions/user-preference-definition.ts
@@ -1,6 +1,6 @@
import { type UserPreferenceScopeType } from './user-preference-scope-type'
-export interface UserPreferenceDefinition {
- isOptedInByDefault: boolean
+export type UserPreferenceDefinition = {
allowedScope: UserPreferenceScopeType
+ defaultValue: T
}
diff --git a/src/services/user-preferences/models/definitions/user-preference-definitions.ts b/src/services/user-preferences/models/definitions/user-preference-definitions.ts
index d9c5e752c..bf0cedee3 100644
--- a/src/services/user-preferences/models/definitions/user-preference-definitions.ts
+++ b/src/services/user-preferences/models/definitions/user-preference-definitions.ts
@@ -1,5 +1,5 @@
import { type UserPreferenceDefinition } from './user-preference-definition'
-export type UserPreferenceDefinitions = {
- [Id in UserPreferenceId]?: UserPreferenceDefinition
+export type UserPreferenceDefinitions = {
+ [P in keyof T]: UserPreferenceDefinition
}
diff --git a/src/services/user-preferences/models/definitions/user-preference-per-scope.ts b/src/services/user-preferences/models/definitions/user-preference-per-scope.ts
new file mode 100644
index 000000000..a6ab0c5b3
--- /dev/null
+++ b/src/services/user-preferences/models/definitions/user-preference-per-scope.ts
@@ -0,0 +1,5 @@
+import { type UserPreferenceScope } from '../storage-models'
+
+export type UserPreferencesPerScope = {
+ [K in UserPreferenceScope]?: T
+}
diff --git a/src/services/user-preferences/models/storage-models/index.ts b/src/services/user-preferences/models/storage-models/index.ts
index 5153d8d6d..51c39a096 100644
--- a/src/services/user-preferences/models/storage-models/index.ts
+++ b/src/services/user-preferences/models/storage-models/index.ts
@@ -1,3 +1 @@
-export * from './user-preference'
export * from './user-preference-scope'
-export * from './user-preferences'
diff --git a/src/services/user-preferences/models/storage-models/user-preference.ts b/src/services/user-preferences/models/storage-models/user-preference.ts
deleted file mode 100644
index 56f487db5..000000000
--- a/src/services/user-preferences/models/storage-models/user-preference.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export interface UserPreference {
- optedIn: boolean
-}
diff --git a/src/services/user-preferences/models/storage-models/user-preferences.ts b/src/services/user-preferences/models/storage-models/user-preferences.ts
deleted file mode 100644
index 95b255d78..000000000
--- a/src/services/user-preferences/models/storage-models/user-preferences.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { type UserPreferenceScope } from './user-preference-scope'
-import { type UserPreference } from './user-preference'
-
-export type UserPreferences = {
- [K in UserPreferenceScope]?: { [Id in UserPreferenceId]: UserPreference }
-}
diff --git a/src/services/user-preferences/models/user-preferences/composite-user-preferences.ts b/src/services/user-preferences/models/user-preferences/composite-user-preferences.ts
deleted file mode 100644
index 30ebb2113..000000000
--- a/src/services/user-preferences/models/user-preferences/composite-user-preferences.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { type UserPreference } from '../storage-models/user-preference'
-
-export type CompositeUserPreferences = {
- [Id in UserPreferenceId]: UserPreference
-}
diff --git a/src/services/user-preferences/user-preferences-service.spec.ts b/src/services/user-preferences/user-preferences-service.spec.ts
index 212303cd9..1eb8e0998 100644
--- a/src/services/user-preferences/user-preferences-service.spec.ts
+++ b/src/services/user-preferences/user-preferences-service.spec.ts
@@ -2,32 +2,70 @@
import * as Cookies from '../../utils/Cookies'
import { describe, afterEach, it, expect } from 'vitest'
-import { UserPreferencesService } from 'src/services/user-preferences/user-preferences'
+import { UserPreferencesService } from 'src/services/user-preferences/user-preferences-service'
import { type UserPreferenceScope } from 'src/services/user-preferences/models/storage-models/user-preference-scope'
-import { type UserPreferences } from 'src/services/user-preferences/models/storage-models/user-preferences'
-import { type UserPreferenceDefinitions } from 'src/services/user-preferences/models/definitions/user-preference-definitions'
-import { CompositeUserPreferencesService } from 'src/services/user-preferences/composite-user-preferences-service'
import { UserPreferenceScopeType } from 'src/services/user-preferences/models/definitions/user-preference-scope-type'
import { type Sync } from 'factory.ts'
import { faker } from '@faker-js/faker'
+import { type UserPreferenceDefinitions } from 'src/components'
+import { type UserPreferencesPerScope } from './models/definitions/user-preference-per-scope'
+
+export type TestType = {
+ Default: { isOptedIn: boolean }
+ PreferenceOne: { isOptedIn: boolean }
+}
+
+describe('When testing the User Preferences Service', () => {
+ it('', async () => {
+ const definitions: UserPreferenceDefinitions = {
+ Default: { allowedScope: UserPreferenceScopeType.Global, defaultValue: { isOptedIn: faker.datatype.boolean() } },
+ PreferenceOne: {
+ allowedScope: UserPreferenceScopeType.Global,
+ defaultValue: { isOptedIn: faker.datatype.boolean() },
+ },
+ }
+
+ const userPreferencesService = new UserPreferencesService(definitions, '1-1-1', { key: 'mp_u_p' })
+
+ await userPreferencesService.init()
+
+ expect((await userPreferencesService.getPreference('Default')).isOptedIn).toBe(
+ definitions.Default.defaultValue.isOptedIn,
+ )
+ expect((await userPreferencesService.getPreference('PreferenceOne')).isOptedIn).toBe(
+ definitions.PreferenceOne.defaultValue.isOptedIn,
+ )
+
+ await userPreferencesService.setPreference('Default', { isOptedIn: !definitions.Default.defaultValue.isOptedIn })
+ await userPreferencesService.setPreference('PreferenceOne', {
+ isOptedIn: !definitions.PreferenceOne.defaultValue.isOptedIn,
+ })
+
+ expect((await userPreferencesService.getPreference('Default')).isOptedIn).toBe(
+ !definitions.Default.defaultValue.isOptedIn,
+ )
+ expect((await userPreferencesService.getPreference('PreferenceOne')).isOptedIn).toBe(
+ !definitions.PreferenceOne.defaultValue.isOptedIn,
+ )
+ })
+})
describe('When testing the User Preferences Service', () => {
- let userPreferencesService: UserPreferencesService
+ let userPreferencesService: UserPreferencesService
const cookieKey = 'mp_u_p'
const lowLevelScope: UserPreferenceScope = '1-1-1'
- let userPreferences: UserPreferences
+ let userPreferencesPerScope: UserPreferencesPerScope
- let definitions: UserPreferenceDefinitions
- const compositeUserPreferencesService = new CompositeUserPreferencesService()
+ let definitions: UserPreferenceDefinitions
function setupPreferencesWithScope(
- definition: UserPreferenceDefinitions,
+ definition: UserPreferenceDefinitions,
scope: UserPreferenceScope | undefined,
): void {
const scopedPreference = makeBuilderFromDefinition(definition, scope)
- userPreferences = TestUserPreferencesFakeFactory([scopedPreference]) as UserPreferences
+ userPreferencesPerScope = TestUserPreferencesFakeFactory([scopedPreference]) as UserPreferencesPerScope
- Cookies.putObject(cookieKey, userPreferences)
+ Cookies.putObject(cookieKey, userPreferencesPerScope)
}
afterEach(() => {
@@ -42,79 +80,63 @@ describe('When testing the User Preferences Service', () => {
])('it should read preferences when there are scoped prefs', async (allowedScope: UserPreferenceScopeType) => {
// arrange
definitions = TestUserPreferenceDefinitionsFakeFactory([
- { id: TestUserPreferenceId.Default, allowedScope },
- { id: TestUserPreferenceId.PreferenceOne, allowedScope },
- ]) as UserPreferenceDefinitions
+ { id: 'Default', allowedScope },
+ { id: 'PreferenceOne', allowedScope },
+ ]) as UserPreferenceDefinitions
setupPreferencesWithScope(definitions, lowLevelScope)
- userPreferencesService = new UserPreferencesService(
- definitions,
- compositeUserPreferencesService,
- lowLevelScope,
- { key: cookieKey },
- )
+ userPreferencesService = new UserPreferencesService(definitions, lowLevelScope, { key: cookieKey })
await userPreferencesService.init()
- const scopedPreferences = userPreferences[lowLevelScope]
+ const scopedPreferences = userPreferencesPerScope[lowLevelScope]
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
- for (const [id, preference] of Object.entries(scopedPreferences)) {
+ for (const [id, expectedIsOptedIn] of Object.entries(scopedPreferences)) {
// act
- const isOptedIn = await userPreferencesService.isOptedIn(id as TestUserPreferenceId)
+ const isOptedIn = await userPreferencesService.getPreference(id as keyof TestType)
// assert
- const expectedIsOptedIn = preference.optedIn
- expect(isOptedIn).not.toBeNull()
- expect(isOptedIn).toBe(expectedIsOptedIn)
+ expect(isOptedIn?.isOptedIn).not.toBeNull()
+ expect(isOptedIn.isOptedIn).toBe(expectedIsOptedIn.isOptedIn)
}
})
it('it should read preferences when there are no scoped prefs', async () => {
// arrange
- definitions = TestUserPreferenceDefinitionsFakeFactory() as UserPreferenceDefinitions
+ definitions = TestUserPreferenceDefinitionsFakeFactory() as UserPreferenceDefinitions
const someScope = '1'
setupPreferencesWithScope(definitions, someScope)
const currentScope = '2'
- userPreferencesService = new UserPreferencesService(
- definitions,
- compositeUserPreferencesService,
- currentScope,
- { key: cookieKey },
- )
+ userPreferencesService = new UserPreferencesService(definitions, currentScope, { key: cookieKey })
await userPreferencesService.init()
for (const [id, definition] of Object.entries(definitions)) {
// act
- const isOptedIn = await userPreferencesService.isOptedIn(id as TestUserPreferenceId)
+ const isOptedIn = await userPreferencesService.getPreference(id as keyof TestType)
// assert
- const expectedIsOptedIn = definition.isOptedInByDefault
- expect(isOptedIn).not.toBeNull()
- expect(isOptedIn).toBe(expectedIsOptedIn)
+ const expectedIsOptedIn = definition.defaultValue
+ expect(isOptedIn?.isOptedIn).not.toBeNull()
+ expect(isOptedIn.isOptedIn).toBe(expectedIsOptedIn.isOptedIn)
}
})
it("it should throw when the preference can't be found", async () => {
// arrange
- definitions = TestUserPreferenceDefinitionsFakeFactory() as UserPreferenceDefinitions
+ definitions = TestUserPreferenceDefinitionsFakeFactory() as UserPreferenceDefinitions
const someScope = '1'
setupPreferencesWithScope(definitions, someScope)
- userPreferencesService = new UserPreferencesService(
- definitions,
- compositeUserPreferencesService,
- someScope,
- { key: cookieKey },
- )
+ userPreferencesService = new UserPreferencesService(definitions, someScope, { key: cookieKey })
await userPreferencesService.init()
// act
const unknownId = 'unknown'
- const isOptedInDelegate = userPreferencesService.isOptedIn.bind(
+ const isOptedInDelegate = userPreferencesService.getPreference.bind(
userPreferencesService,
// @ts-expect-error - we are testing an incorrect usage
unknownId,
@@ -134,108 +156,93 @@ describe('When testing the User Preferences Service', () => {
'it should be able to update a preferences when the preference exists',
async (expectedScope, allowedScope: UserPreferenceScopeType) => {
// arrange
- const userPreferenceId = TestUserPreferenceId.Default
+ const userPreferenceId = 'Default'
const testOptedInState = true
definitions = TestUserPreferenceDefinitionsFakeFactory([
{ id: userPreferenceId, allowedScope, isOptedInByDefault: testOptedInState },
- ]) as UserPreferenceDefinitions
+ ]) as UserPreferenceDefinitions
setupPreferencesWithScope(definitions, expectedScope as UserPreferenceScope)
- userPreferencesService = new UserPreferencesService(
- definitions,
- compositeUserPreferencesService,
- lowLevelScope,
- { key: cookieKey },
- )
+ userPreferencesService = new UserPreferencesService(definitions, lowLevelScope, { key: cookieKey })
await userPreferencesService.init()
// pre-assert
- const testState = await userPreferencesService.isOptedIn(userPreferenceId)
+ const testState = await userPreferencesService.getPreference(userPreferenceId)
- expect(testState).toBe(testOptedInState)
+ expect(testState.isOptedIn).toBe(testOptedInState)
// act
const expectedOptedInState = !testOptedInState
- await userPreferencesService.setPreference(userPreferenceId, expectedOptedInState)
+ await userPreferencesService.setPreference(userPreferenceId, { isOptedIn: expectedOptedInState })
// assert
- const actualState = await userPreferencesService.isOptedIn(userPreferenceId)
+ const actualState = await userPreferencesService.getPreference(userPreferenceId)
- expect(actualState).toBe(expectedOptedInState)
+ expect(actualState.isOptedIn).toBe(expectedOptedInState)
},
)
it('it should be able to update a preferences when the preference does not exist', async () => {
// arrange
- const userPreferenceId = TestUserPreferenceId.Default
+ const userPreferenceId = 'Default'
const allowedScope = UserPreferenceScopeType.LevelThreeScope
const testOptedInState = true
definitions = TestUserPreferenceDefinitionsFakeFactory([
{ id: userPreferenceId, allowedScope, isOptedInByDefault: testOptedInState },
- ]) as UserPreferenceDefinitions
+ ]) as UserPreferenceDefinitions
- userPreferencesService = new UserPreferencesService(
- definitions,
- compositeUserPreferencesService,
- lowLevelScope,
- { key: cookieKey },
- )
+ userPreferencesService = new UserPreferencesService(definitions, lowLevelScope, { key: cookieKey })
await userPreferencesService.init()
// pre-assert
- const testState = await userPreferencesService.isOptedIn(userPreferenceId)
+ const testState = await userPreferencesService.getPreference(userPreferenceId)
- expect(testState).toBe(testOptedInState)
+ expect(testState.isOptedIn).toBe(testOptedInState)
// act
const expectedOptedInState = !testOptedInState
- await userPreferencesService.setPreference(userPreferenceId, expectedOptedInState)
+ await userPreferencesService.setPreference(userPreferenceId, { isOptedIn: expectedOptedInState })
// assert
- const actualState = await userPreferencesService.isOptedIn(userPreferenceId)
+ const actualState = await userPreferencesService.getPreference(userPreferenceId)
- expect(actualState).toBe(expectedOptedInState)
+ expect(actualState.isOptedIn).toBe(expectedOptedInState)
})
})
})
-export enum TestUserPreferenceId {
- Default = 'default-id',
- PreferenceOne = 'preference-one',
-}
-
export function TestUserPreferenceDefinitionsFakeFactory(
config?: Array<{
- id: TestUserPreferenceId
+ id: string
isOptedInByDefault?: boolean
allowedScope?: UserPreferenceScopeType
}>,
-): Sync.Builder<
- UserPreferenceDefinitions,
- keyof UserPreferenceDefinitions
-> {
+): Sync.Builder, keyof UserPreferenceDefinitions> {
if (!config) {
- config = Object.values(TestUserPreferenceId).map(id => ({ id }))
+ config = [{ id: 'Default' }, { id: 'PreferenceOne' }]
}
return config.reduce((definitions, { id, isOptedInByDefault, allowedScope }) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
- definitions[id] = createDefinition({ isOptedInByDefault, allowedScope })
+ definitions[id] = createDefinition({
+ defaultValue: isOptedInByDefault !== undefined ? { isOptedIn: isOptedInByDefault } : undefined,
+ allowedScope,
+ })
return definitions
- }, {})
+ }, {}) as UserPreferenceDefinitions
}
type Definition = {
- isOptedInByDefault?: boolean
+ defaultValue?: { isOptedIn: boolean }
allowedScope?: UserPreferenceScopeType
}
-function createDefinition({ isOptedInByDefault, allowedScope }: Definition = {}): Definition {
+function createDefinition({ defaultValue, allowedScope }: Definition = {}): Definition {
return {
- isOptedInByDefault: isOptedInByDefault ?? faker.datatype.boolean(),
+ defaultValue: defaultValue ?? { isOptedIn: faker.datatype.boolean() },
allowedScope: allowedScope ?? faker.helpers.enumValue(UserPreferenceScopeType),
}
}
@@ -243,49 +250,50 @@ function createDefinition({ isOptedInByDefault, allowedScope }: Definition = {})
export interface TestUserPreferencesFakeBuilder {
wantsRandom?: boolean
scope?: UserPreferenceScope
- userPreferenceIds?: TestUserPreferenceId[]
- optedIns?: boolean[]
+ keys?: string[]
+ defaultValues?: any[]
}
export function makeBuilderFromDefinition(
- definitions: UserPreferenceDefinitions,
+ definitions: UserPreferenceDefinitions,
scope?: UserPreferenceScope,
): TestUserPreferencesFakeBuilder {
return {
scope: scope ?? getRandomScope({ excludeGlobal: true }),
- userPreferenceIds: Object.keys(definitions) as TestUserPreferenceId[],
- optedIns: Object.values(definitions).map(({ isOptedInByDefault }) => isOptedInByDefault),
+ keys: Object.keys(definitions),
+ defaultValues: Object.values(definitions).map(definition => definition.defaultValue),
}
}
export function TestUserPreferencesFakeFactory(
scopes: TestUserPreferencesFakeBuilder[] = [],
-): Sync.Builder, keyof UserPreferences> {
- return scopes.reduce((scopedPreferences, { wantsRandom = false, scope, userPreferenceIds, optedIns }) => {
+): Sync.Builder, keyof UserPreferencesPerScope> {
+ return scopes.reduce((scopedPreferences, { wantsRandom = false, scope, keys, defaultValues }) => {
const effectiveScope = scope ?? getRandomScope({ excludeGlobal: true })
+
if (wantsRandom) {
- const numberOfValues = faker.number.int({ max: Object.keys(TestUserPreferenceId).length, min: 1 })
- userPreferenceIds = Array.from({ length: numberOfValues }, () => faker.helpers.enumValue(TestUserPreferenceId))
- optedIns = Array.from({ length: numberOfValues }, () => faker.datatype.boolean())
+ const numberOfValues = faker.number.int({ max: 2, min: 1 })
+ keys = Array.from({ length: numberOfValues }, () => faker.helpers.arrayElement(['Default', 'PreferenceOne']))
+ defaultValues = Array.from({ length: numberOfValues }, () => faker.datatype.boolean())
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
- scopedPreferences[effectiveScope] = userPreferenceIds.reduce((preferences, userPreferenceId, index) => {
- const effectiveId = userPreferenceId ?? TestUserPreferenceId.Default
+ scopedPreferences[effectiveScope] = keys.reduce((preferences, userPreferenceId, index) => {
+ const effectiveId = userPreferenceId ?? 'Default'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
- const effectiveOptedInState = optedIns[index] ?? faker.datatype.boolean()
+ const effectiveOptedInState = defaultValues[index] ?? { isOptedIn: faker.datatype.boolean() }
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
- preferences[effectiveId] = { optedIn: effectiveOptedInState }
+ preferences[effectiveId] = effectiveOptedInState
return preferences
}, {})
return scopedPreferences
- }, {}) as UserPreferences
+ }, {}) as UserPreferencesPerScope
}
function getRandomScope({ maxScope = 3, excludeGlobal = false }): UserPreferenceScope {
diff --git a/src/services/user-preferences/user-preferences-service.ts b/src/services/user-preferences/user-preferences-service.ts
new file mode 100644
index 000000000..c052d5cec
--- /dev/null
+++ b/src/services/user-preferences/user-preferences-service.ts
@@ -0,0 +1,70 @@
+import { type UserPreferenceScope } from 'src/services/user-preferences/models/storage-models'
+import * as Cookies from 'src/utils/Cookies'
+import { type CookieOptions } from 'src/utils/Cookies'
+import { CompositeUserPreferencesService } from './composite-user-preferences-service'
+import { type UserPreferenceDefinitions } from 'src/components'
+import { type UserPreferencesPerScope } from './models/definitions/user-preference-per-scope'
+
+export class UserPreferencesService> {
+ private preferences!: T
+ private readonly compositeUserPreferencesService: CompositeUserPreferencesService
+ constructor(
+ private readonly definitions: UserPreferenceDefinitions,
+ private readonly currentScope: UserPreferenceScope,
+ private readonly cookieOptions: CookieOptions & { key: string },
+ private readonly onUpdate?: (resolvedPreferences: T) => void,
+ ) {
+ this.compositeUserPreferencesService = new CompositeUserPreferencesService()
+ }
+
+ public async getPreference(key: TKey): Promise {
+ const userPreference = this.preferences[key]
+
+ if (!userPreference) await Promise.reject(new Error(`Invalid Operation. A user preference could not be found.`))
+
+ return userPreference as TValue
+ }
+
+ public async setPreference(key: TKey, value: TValue): Promise {
+ const currentStoredPreferences = await this.getStoredPreferences()
+
+ const storedPreferences = this.compositeUserPreferencesService.getUpdatedUserPreferenceStorageObject(
+ key,
+ value,
+ this.currentScope,
+ currentStoredPreferences,
+ this.definitions[key].allowedScope,
+ )
+
+ await this.setStoredPreferences(storedPreferences)
+
+ this.preferences = this.compositeUserPreferencesService.getScopedUserPreferences(
+ storedPreferences,
+ this.currentScope,
+ this.definitions,
+ )
+
+ this.onUpdate?.(this.preferences)
+ }
+
+ public async init(): Promise {
+ const storedPreferences = await this.getStoredPreferences()
+ this.preferences = this.compositeUserPreferencesService.getScopedUserPreferences(
+ storedPreferences,
+ this.currentScope,
+ this.definitions,
+ )
+
+ this.onUpdate?.(this.preferences)
+ }
+
+ private async getStoredPreferences(): Promise> {
+ return await Promise.resolve(Cookies.getObject(this.cookieOptions.key) ?? {})
+ }
+
+ private async setStoredPreferences(storedPreferences: UserPreferencesPerScope): Promise {
+ Cookies.putObject(this.cookieOptions.key, storedPreferences, this.cookieOptions)
+
+ await Promise.resolve()
+ }
+}
diff --git a/src/services/user-preferences/user-preferences.ts b/src/services/user-preferences/user-preferences.ts
deleted file mode 100644
index 608bb9a1e..000000000
--- a/src/services/user-preferences/user-preferences.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-/* eslint-disable @typescript-eslint/no-extraneous-class,no-unused-vars,@typescript-eslint/no-unused-vars */
-import { type UserPreferences } from 'src/services/user-preferences/models/storage-models/user-preferences'
-import { type CompositeUserPreferences } from 'src/services/user-preferences/models/user-preferences/composite-user-preferences'
-import { type UserPreferenceScope } from 'src/services/user-preferences/models/storage-models/user-preference-scope'
-import { type UserPreferenceDefinitions } from 'src/services/user-preferences/models/definitions/user-preference-definitions'
-import { type CompositeUserPreferencesService } from 'src/services/user-preferences/composite-user-preferences-service'
-import * as Cookies from 'src/utils/Cookies'
-import { type CookieOptions } from 'src/utils/Cookies'
-
-export class UserPreferencesService {
- public preferences!: CompositeUserPreferences
-
- constructor(
- private readonly definitions: UserPreferenceDefinitions,
- private readonly compositeUserPreferencesService: CompositeUserPreferencesService,
- private readonly currentScope: UserPreferenceScope,
- private readonly cookieOptions: CookieOptions & { key: string },
- private readonly onUpdate?: (resolvedPreferences: CompositeUserPreferences) => void,
- ) {}
-
- public async init(): Promise {
- const storedPreferences = await this.getStoredPreferences()
-
- this.preferences = this.compositeUserPreferencesService.getScopedUserPreferences(
- storedPreferences,
- this.currentScope,
- this.definitions,
- )
-
- this.onUpdate?.(this.preferences)
- }
-
- public async isOptedIn(userPreferenceId: TUserPreferenceId): Promise {
- const userPreference = this.preferences[userPreferenceId]
-
- if (!userPreference) await Promise.reject(new Error(`Invalid Operation. A user preference could not be found.`))
-
- return userPreference.optedIn
- }
-
- public async setPreference(userPreferenceId: TUserPreferenceId, isOptedIn: boolean): Promise {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-expect-error
- const { allowedScope } = this.definitions[userPreferenceId]
-
- const currentStoredPreferences = Cookies.getObject(this.cookieOptions.key)
-
- const storedPreferences = this.compositeUserPreferencesService.getUpdatedUserPreferenceStorageObject(
- userPreferenceId,
- isOptedIn,
- this.currentScope,
- currentStoredPreferences as unknown as UserPreferences,
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
- allowedScope,
- )
-
- await this.setStoredPreferences(storedPreferences)
-
- this.preferences = this.compositeUserPreferencesService.getScopedUserPreferences(
- storedPreferences,
- this.currentScope,
- this.definitions,
- )
-
- this.onUpdate?.(this.preferences)
-
- // eslint-disable-next-line @typescript-eslint/return-await
- return Promise.resolve()
- }
-
- private async getStoredPreferences(): Promise> {
- return await Promise.resolve(Cookies.getObject(this.cookieOptions.key) ?? {})
- }
-
- private async setStoredPreferences(storedPreferences: UserPreferences): Promise {
- Cookies.putObject(this.cookieOptions.key, storedPreferences, this.cookieOptions)
-
- await Promise.resolve()
- }
-}
diff --git a/src/types/icons.ts b/src/types/icons.ts
index c7dfd502e..03a2426ff 100644
--- a/src/types/icons.ts
+++ b/src/types/icons.ts
@@ -36,6 +36,7 @@ export type IconNames =
| 'connections'
| 'conversion'
| 'copy'
+ | 'crosshair'
| 'dashboard'
| 'database'
| 'dataPlatform'
@@ -79,6 +80,8 @@ export type IconNames =
| 'myHub'
| 'next'
| 'notification'
+ | 'notificationSubscribed'
+ | 'notificationSubscribe'
| 'observability'
| 'openTab'
| 'organization'