diff --git a/.storybook/preview.ts b/.storybook/preview.ts
index 95c8a4f..e4aa6b2 100644
--- a/.storybook/preview.ts
+++ b/.storybook/preview.ts
@@ -10,6 +10,8 @@
* You should have received a copy of the GNU Affero General Public License along with this program. If not, see .
*/
+import { computePosition, autoUpdate, offset, shift, flip, arrow } from '@floating-ui/dom';
+import { initializeStores, storePopup } from '@skeletonlabs/skeleton';
import type { Preview } from '@storybook/svelte';
import { init, locale, register } from 'svelte-i18n';
@@ -39,6 +41,17 @@ function withI18n(storyFn, context) {
return storyFn();
}
+function withStores(storyFn) {
+ // Globally initialize stores for Skeleton.dev contexts
+ initializeStores();
+ return storyFn();
+}
+
+function withPopup(storyFn) {
+ storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
+ return storyFn();
+}
+
const preview: Preview = {
globalTypes: {
locale: {
@@ -82,6 +95,6 @@ const preview: Preview = {
}
};
-export const decorators = [withI18n];
+export const decorators = [withI18n, withPopup, withStores];
export default preview;
diff --git a/package-lock.json b/package-lock.json
index 07f7f32..38220f9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "anvil",
- "version": "0.0.7",
+ "version": "0.0.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "anvil",
- "version": "0.0.7",
+ "version": "0.0.8",
"dependencies": {
"axios": "1.7.2",
"cheerio": "1.0.0-rc.12"
@@ -4815,86 +4815,6 @@
"@tauri-apps/cli-win32-x64-msvc": "1.6.0"
}
},
- "node_modules/@tauri-apps/cli-darwin-arm64": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.6.0.tgz",
- "integrity": "sha512-SNRwUD9nqGxY47mbY1CGTt/jqyQOU7Ps7Mx/mpgahL0FVUDiCEY/5L9QfEPPhEgccgcelEVn7i6aQHIkHyUtCA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tauri-apps/cli-darwin-x64": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.6.0.tgz",
- "integrity": "sha512-g2/uDR/eeH2arvuawA4WwaEOqv/7jDO/ZLNI3JlBjP5Pk8GGb3Kdy0ro1xQzF94mtk2mOnOXa4dMgAet4sUJ1A==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.6.0.tgz",
- "integrity": "sha512-EVwf4oRkQyG8BpSrk0gqO7oA0sDM2MdNDtJpMfleYFEgCxLIOGZKNqaOW3M7U+0Y4qikmG3TtRK+ngc8Ymtrjg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tauri-apps/cli-linux-arm64-gnu": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.6.0.tgz",
- "integrity": "sha512-YdpY17cAySrhK9dX4BUVEmhAxE2o+6skIEFg8iN/xrDwRxhaNPI9I80YXPatUTX54Kx55T5++25VJG9+3iw83A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tauri-apps/cli-linux-arm64-musl": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.6.0.tgz",
- "integrity": "sha512-4U628tuf2U8pMr4tIBJhEkrFwt+46dwhXrDlpdyWSZtnop5RJAVKHODm0KbWns4xGKfTW1F3r6sSv+2ZxLcISA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
"node_modules/@tauri-apps/cli-linux-x64-gnu": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.6.0.tgz",
@@ -4927,54 +4847,6 @@
"node": ">= 10"
}
},
- "node_modules/@tauri-apps/cli-win32-arm64-msvc": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-1.6.0.tgz",
- "integrity": "sha512-QwWpWk4ubcwJ1rljsRAmINgB2AwkyzZhpYbalA+MmzyYMREcdXWGkyixWbRZgqc6fEWEBmq5UG73qz5eBJiIKg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tauri-apps/cli-win32-ia32-msvc": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.6.0.tgz",
- "integrity": "sha512-Vtw0yxO9+aEFuhuxQ57ALG43tjECopRimRuKGbtZYDCriB/ty5TrT3QWMdy0dxBkpDTu3Rqsz30sbDzw6tlP3Q==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tauri-apps/cli-win32-x64-msvc": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.6.0.tgz",
- "integrity": "sha512-h54FHOvGi7+LIfRchzgZYSCHB1HDlP599vWXQQJ/XnwJY+6Rwr2E5bOe/EhqoG8rbGkfK0xX3KPAvXPbUlmggg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
"node_modules/@testing-library/dom": {
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.3.0.tgz",
@@ -8878,20 +8750,6 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
- "node_modules/fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -14566,20 +14424,6 @@
"@esbuild/win32-x64": "0.21.5"
}
},
- "node_modules/vite/node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
"node_modules/vitefu": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz",
diff --git a/package.json b/package.json
index 623953d..b71c6bf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "anvil",
- "version": "0.0.7",
+ "version": "0.0.8",
"private": true,
"scripts": {
"dev": "vite dev",
diff --git a/src/lib/components/atoms/MainMenuSummary.svelte b/src/lib/components/atoms/MainMenuSummary.svelte
new file mode 100644
index 0000000..e6da269
--- /dev/null
+++ b/src/lib/components/atoms/MainMenuSummary.svelte
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/i18n/locales/bg.json b/src/lib/i18n/locales/bg.json
index 943ea24..e71a8cc 100644
--- a/src/lib/i18n/locales/bg.json
+++ b/src/lib/i18n/locales/bg.json
@@ -90,6 +90,14 @@
},
"heading": ""
},
+ "menu": {
+ "buttons": {
+ "avatar": "",
+ "issues": "",
+ "notifications": "",
+ "prs": ""
+ }
+ },
"projects": {
"actions": {
"fork": "",
diff --git a/src/lib/i18n/locales/de.json b/src/lib/i18n/locales/de.json
index cf731cc..addfc7e 100644
--- a/src/lib/i18n/locales/de.json
+++ b/src/lib/i18n/locales/de.json
@@ -90,6 +90,14 @@
},
"heading": "Aktivitäten"
},
+ "menu": {
+ "buttons": {
+ "avatar": "",
+ "issues": "",
+ "notifications": "",
+ "prs": ""
+ }
+ },
"projects": {
"actions": {
"fork": "Fork",
diff --git a/src/lib/i18n/locales/en.json b/src/lib/i18n/locales/en.json
index c6be5a1..4108365 100644
--- a/src/lib/i18n/locales/en.json
+++ b/src/lib/i18n/locales/en.json
@@ -90,6 +90,14 @@
},
"heading": "Activities"
},
+ "menu": {
+ "buttons": {
+ "avatar": "Menu",
+ "issues": "{number}{blockElementOpen}{number, plural, one{ issue} other{ issues}} open{blockElementClose}",
+ "notifications": "{number}{blockElementOpen} unread {number, plural, one{ notification} other{ notifications}}{blockElementClose}",
+ "prs": "{number}{blockElementOpen}{number, plural, one{ pull request} other{ pull requests}} open{blockElementClose}"
+ }
+ },
"projects": {
"actions": {
"fork": "Fork",
diff --git a/src/lib/i18n/locales/he.json b/src/lib/i18n/locales/he.json
index b72aa4f..4cb4413 100644
--- a/src/lib/i18n/locales/he.json
+++ b/src/lib/i18n/locales/he.json
@@ -90,6 +90,14 @@
},
"heading": "פעילות"
},
+ "menu": {
+ "buttons": {
+ "avatar": "",
+ "issues": "",
+ "notifications": "",
+ "prs": ""
+ }
+ },
"projects": {
"actions": {
"fork": "",
diff --git a/src/lib/i18n/locales/pl.json b/src/lib/i18n/locales/pl.json
index b36aae5..4ea8ff3 100644
--- a/src/lib/i18n/locales/pl.json
+++ b/src/lib/i18n/locales/pl.json
@@ -90,6 +90,14 @@
},
"heading": "Aktywność"
},
+ "menu": {
+ "buttons": {
+ "avatar": "",
+ "issues": "",
+ "notifications": "",
+ "prs": ""
+ }
+ },
"projects": {
"actions": {
"fork": "",
diff --git a/stories/atoms/MainMenuSummary.stories.ts b/stories/atoms/MainMenuSummary.stories.ts
new file mode 100644
index 0000000..6c2d4ed
--- /dev/null
+++ b/stories/atoms/MainMenuSummary.stories.ts
@@ -0,0 +1,26 @@
+/* Stories for MainMenuSummary atom.
+ * Copyright (C) 2024 André Jaenisch
+ * SPDX-FileCopyrightText: 2024 André Jaenisch
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along with this program. If not, see .
+ */
+
+import type { Meta, StoryObj } from '@storybook/svelte';
+
+import MainMenuSummary from '$lib/components/atoms/MainMenuSummary.svelte';
+
+const meta = {
+ title: 'Atoms/MainMenuSummary',
+ component: MainMenuSummary,
+ tags: ['autodocs']
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Plain: Story = {};
diff --git a/tests/components/atoms/MainMenuSummary.test.ts b/tests/components/atoms/MainMenuSummary.test.ts
new file mode 100644
index 0000000..619ed8e
--- /dev/null
+++ b/tests/components/atoms/MainMenuSummary.test.ts
@@ -0,0 +1,128 @@
+/* Component test for MainMenuSummary atom.
+ * Copyright (C) 2024 André Jaenisch
+ * SPDX-FileCopyrightText: 2024 André Jaenisch
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License along with this program. If not, see .
+ */
+
+import { computePosition, autoUpdate, offset, shift, flip, arrow } from '@floating-ui/dom';
+import { storePopup } from '@skeletonlabs/skeleton';
+import '@testing-library/jest-dom';
+import { render, screen } from '@testing-library/svelte';
+import { get, readable } from 'svelte/store';
+import { init, locale, register } from 'svelte-i18n';
+
+import MainMenuSummary from '../../../src/lib/components/atoms/MainMenuSummary.svelte';
+import enMessages from '../../../src/lib/i18n/locales/en.json';
+
+describe('MainMenuSummary.svelte', () => {
+ beforeEach(() => {
+ register('en', () => import('../../../src/lib/i18n/locales/en.json'));
+ init({ fallbackLocale: 'en', initialLocale: 'en' });
+ locale.set('en');
+ });
+
+ it('should mount', () => {
+ // Arrange
+ // See https://testing-library.com/docs/svelte-testing-library/example#contexts
+ const modalStore = readable([]);
+ // See https://www.skeleton.dev/utilities/popups
+ storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
+
+ // Act
+ const { container } = render(MainMenuSummary, {
+ context: new Map([
+ ['modalStore', modalStore],
+ ['storePopup', storePopup]
+ ])
+ });
+
+ // Assert
+ expect(container).toBeTruthy();
+ });
+
+ it('should have an avatar', () => {
+ // Arrange
+ // See https://testing-library.com/docs/svelte-testing-library/example#contexts
+ const modalStore = readable([]);
+ // See https://www.skeleton.dev/utilities/popups
+ storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
+
+ // Act
+ render(MainMenuSummary, {
+ context: new Map([
+ ['modalStore', modalStore],
+ ['storePopup', storePopup]
+ ])
+ });
+
+ // Assert
+ expect(
+ screen.getByRole('button', {
+ name: enMessages.page.profile.menu.buttons.avatar
+ })
+ ).toBeInTheDocument();
+ });
+
+ it('should have an indicator for open issues', () => {
+ // Arrange
+ // See https://testing-library.com/docs/svelte-testing-library/example#contexts
+ const modalStore = readable([]);
+ // See https://www.skeleton.dev/utilities/popups
+ storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
+
+ // Act
+ render(MainMenuSummary, {
+ context: new Map([
+ ['modalStore', modalStore],
+ ['storePopup', storePopup]
+ ])
+ });
+
+ // Assert
+ expect(screen.getByText('issues open')).toBeInTheDocument();
+ });
+
+ it('should have an indicator for open issues', () => {
+ // Arrange
+ // See https://testing-library.com/docs/svelte-testing-library/example#contexts
+ const modalStore = readable([]);
+ // See https://www.skeleton.dev/utilities/popups
+ storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
+
+ // Act
+ render(MainMenuSummary, {
+ context: new Map([
+ ['modalStore', modalStore],
+ ['storePopup', storePopup]
+ ])
+ });
+
+ // Assert
+ expect(screen.getByText('pull request open')).toBeInTheDocument();
+ });
+
+ it('should have an indicator for unread notifications', () => {
+ // Arrange
+ // See https://testing-library.com/docs/svelte-testing-library/example#contexts
+ const modalStore = readable([]);
+ // See https://www.skeleton.dev/utilities/popups
+ storePopup.set({ computePosition, autoUpdate, offset, shift, flip, arrow });
+
+ // Act
+ render(MainMenuSummary, {
+ context: new Map([
+ ['modalStore', modalStore],
+ ['storePopup', storePopup]
+ ])
+ });
+
+ // Assert
+ expect(screen.getByText('unread notifications')).toBeInTheDocument();
+ });
+});