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(); + }); +});