From 8f798c8b351dfbe37ecaf2a8b823c9e5d0457de9 Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Fri, 28 Jun 2024 04:56:53 +0100 Subject: [PATCH 01/16] feat: slider component --- package.json | 2 + pnpm-lock.yaml | 240 +++++++++++++++++++++++++++ src/components/fields/RadioGroup.tsx | 4 +- src/components/fields/Slider.tsx | 67 ++++++++ src/utils/cn.ts | 3 +- 5 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 src/components/fields/Slider.tsx diff --git a/package.json b/package.json index d32fbe36c..170dbfb92 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "dependencies": { "@electron/remote": "2.1.2", "@primer/octicons-react": "19.10.0", + "@radix-ui/react-slider": "^1.2.0", "axios": "1.7.2", "clsx": "2.1.1", "date-fns": "3.6.0", @@ -113,6 +114,7 @@ "react-dom": "18.3.1", "react-final-form": "6.5.9", "react-router-dom": "6.24.0", + "tailwind-merge": "^2.3.0", "ts-loader": "9.5.1", "typescript": "5.5.2" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05be5db32..c9e4276f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@primer/octicons-react': specifier: 19.10.0 version: 19.10.0(react@18.3.1) + '@radix-ui/react-slider': + specifier: ^1.2.0 + version: 1.2.0(@types/react-dom@18.2.22)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) axios: specifier: 1.7.2 version: 1.7.2 @@ -47,6 +50,9 @@ importers: react-router-dom: specifier: 6.24.0 version: 6.24.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tailwind-merge: + specifier: ^2.3.0 + version: 2.3.0 ts-loader: specifier: 9.5.1 version: 9.5.1(typescript@5.5.2)(webpack@5.92.1(webpack-cli@5.1.4)) @@ -548,6 +554,132 @@ packages: peerDependencies: react: '>=16.3' + '@radix-ui/number@1.1.0': + resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} + + '@radix-ui/primitive@1.1.0': + resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} + + '@radix-ui/react-collection@1.1.0': + resolution: {integrity: sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.0': + resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.0': + resolution: {integrity: sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-direction@1.1.0': + resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-primitive@2.0.0': + resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slider@1.2.0': + resolution: {integrity: sha512-dAHCDA4/ySXROEPaRtaMV5WHL8+JB/DbtyTbJjYkY0RXmKMO2Ln8DFZhywG5/mVQ4WqHDBc8smc14yPXPqZHYA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.1.0': + resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.0': + resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.1.0': + resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.0': + resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.0': + resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.0': + resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@remix-run/router@1.17.0': resolution: {integrity: sha512-2D6XaHEVvkCn682XBnipbJjgZUU7xjLtA4dGJRBVUKpEaDYOZMENZoZjAOSb7qirxt5RupjzZxz4fK2FO+EFPw==} engines: {node: '>=14.0.0'} @@ -2812,6 +2944,9 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tailwind-merge@2.3.0: + resolution: {integrity: sha512-vkYrLpIP+lgR0tQCG6AP7zZXCTLc1Lnv/CCRT3BqJ9CZ3ui2++GPaGb1x/ILsINIMSYqqvrpqjUFsMNLlW99EA==} + tailwindcss@3.4.4: resolution: {integrity: sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==} engines: {node: '>=14.0.0'} @@ -3718,6 +3853,107 @@ snapshots: dependencies: react: 18.3.1 + '@radix-ui/number@1.1.0': {} + + '@radix-ui/primitive@1.1.0': {} + + '@radix-ui/react-collection@1.1.0(@types/react-dom@18.2.22)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.22)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.2.22 + + '@radix-ui/react-compose-refs@1.1.0(@types/react@18.3.3)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + + '@radix-ui/react-context@1.1.0(@types/react@18.3.3)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + + '@radix-ui/react-direction@1.1.0(@types/react@18.3.3)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + + '@radix-ui/react-primitive@2.0.0(@types/react-dom@18.2.22)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.2.22 + + '@radix-ui/react-slider@1.2.0(@types/react-dom@18.2.22)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.2.22)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.22)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.3)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.2.22 + + '@radix-ui/react-slot@1.1.0(@types/react@18.3.3)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.3)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.3)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.3)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + + '@radix-ui/react-use-previous@1.1.0(@types/react@18.3.3)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + + '@radix-ui/react-use-size@1.1.0(@types/react@18.3.3)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + '@remix-run/router@1.17.0': {} '@sinclair/typebox@0.27.8': {} @@ -6263,6 +6499,10 @@ snapshots: symbol-tree@3.2.4: {} + tailwind-merge@2.3.0: + dependencies: + '@babel/runtime': 7.24.1 + tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.2)): dependencies: '@alloc/quick-lru': 5.2.0 diff --git a/src/components/fields/RadioGroup.tsx b/src/components/fields/RadioGroup.tsx index 3ea7fe774..99430f8b7 100644 --- a/src/components/fields/RadioGroup.tsx +++ b/src/components/fields/RadioGroup.tsx @@ -1,5 +1,6 @@ import type { ChangeEvent, FC, ReactNode } from 'react'; import type { RadioGroupItem } from '../../types'; +import { cn } from '../../utils/cn'; export interface IRadioGroup { name: string; @@ -9,11 +10,12 @@ export interface IRadioGroup { value: string; disabled?: boolean; onChange: (event: ChangeEvent) => void; + className?: string; } export const RadioGroup: FC = (props: IRadioGroup) => { return ( -
+
From a4bf5564cb3c9570c7e1f1fb5ad3f048ea711cdf Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Mon, 1 Jul 2024 15:14:08 +0100 Subject: [PATCH 05/16] fix: consistent ui --- src/components/fields/Slider.tsx | 6 +++--- src/components/settings/AppearanceSettings.tsx | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/fields/Slider.tsx b/src/components/fields/Slider.tsx index 514dfb4c7..4ee323f74 100644 --- a/src/components/fields/Slider.tsx +++ b/src/components/fields/Slider.tsx @@ -32,13 +32,13 @@ export const Slider = forwardRef< > {name && ( )} - + @@ -47,7 +47,7 @@ export const Slider = forwardRef< min / step && i + 1 < (max - min) / step, })} diff --git a/src/components/settings/AppearanceSettings.tsx b/src/components/settings/AppearanceSettings.tsx index 17b40fb9d..8f14fcae3 100644 --- a/src/components/settings/AppearanceSettings.tsx +++ b/src/components/settings/AppearanceSettings.tsx @@ -42,6 +42,7 @@ export const AppearanceSettings: FC = () => { onChange={(evt) => { updateSetting('theme', evt.target.value); }} + className="mb-1" /> Date: Mon, 1 Jul 2024 15:14:41 +0100 Subject: [PATCH 06/16] chore: revert electron mock --- src/__mocks__/electron.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__mocks__/electron.js b/src/__mocks__/electron.js index 04277d63a..c2d0e68c2 100644 --- a/src/__mocks__/electron.js +++ b/src/__mocks__/electron.js @@ -40,7 +40,7 @@ module.exports = { case 'get-platform': return Promise.resolve('darwin'); case 'gitify:version': - return Promise.resolve('dev'); + return Promise.resolve('0.0.1'); default: return Promise.reject(new Error(`Unknown channel: ${channel}`)); } From b0b36cdc9e90b302824d0852e0cbba494d4a56c6 Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Mon, 1 Jul 2024 15:15:28 +0100 Subject: [PATCH 07/16] fix: consistent test output --- .../settings/AppearanceSettings.test.tsx | 2 ++ .../settings/SettingsFooter.test.tsx | 24 ++++++++++--------- .../SettingsFooter.test.tsx.snap | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/components/settings/AppearanceSettings.test.tsx b/src/components/settings/AppearanceSettings.test.tsx index 96cae84b7..16df7eeb0 100644 --- a/src/components/settings/AppearanceSettings.test.tsx +++ b/src/components/settings/AppearanceSettings.test.tsx @@ -4,6 +4,8 @@ import { mockAuth, mockSettings } from '../../__mocks__/state-mocks'; import { AppContext } from '../../context/App'; import { AppearanceSettings } from './AppearanceSettings'; +global.ResizeObserver = require('resize-observer-polyfill'); + describe('routes/components/AppearanceSettings.tsx', () => { const updateSetting = jest.fn(); diff --git a/src/components/settings/SettingsFooter.test.tsx b/src/components/settings/SettingsFooter.test.tsx index 546c75997..57755ddac 100644 --- a/src/components/settings/SettingsFooter.test.tsx +++ b/src/components/settings/SettingsFooter.test.tsx @@ -11,24 +11,22 @@ jest.mock('react-router-dom', () => ({ useNavigate: () => mockNavigate, })); +global.ResizeObserver = require('resize-observer-polyfill'); + describe('routes/components/SettingsFooter.tsx', () => { afterEach(() => { jest.clearAllMocks(); + process.env = originalEnv; }); - describe('app version', () => { - let originalEnv: NodeJS.ProcessEnv; - - beforeEach(() => { - // Save the original node env state - originalEnv = process.env; - }); + let originalEnv: NodeJS.ProcessEnv; - afterEach(() => { - // Restore the original node env state - process.env = originalEnv; - }); + beforeEach(() => { + // Save the original node env state + originalEnv = process.env; + }); + describe('app version', () => { it('should show production app version', async () => { process.env = { ...originalEnv, @@ -79,6 +77,10 @@ describe('routes/components/SettingsFooter.tsx', () => { }); it('should open release notes', async () => { + process.env = { + ...originalEnv, + NODE_ENV: 'production', + }; const openExternalLinkMock = jest.spyOn(comms, 'openExternalLink'); await act(async () => { diff --git a/src/components/settings/__snapshots__/SettingsFooter.test.tsx.snap b/src/components/settings/__snapshots__/SettingsFooter.test.tsx.snap index c7a8ec035..75e06fc2a 100644 --- a/src/components/settings/__snapshots__/SettingsFooter.test.tsx.snap +++ b/src/components/settings/__snapshots__/SettingsFooter.test.tsx.snap @@ -14,6 +14,6 @@ exports[`routes/components/SettingsFooter.tsx app version should show production title="app-version" > Gitify - vdev + v0.0.1 `; From aa7889901550eebda1eda3c44d256f1d3d67e008 Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Mon, 1 Jul 2024 15:50:01 +0100 Subject: [PATCH 08/16] feat: add `%` & alignment --- src/components/fields/Slider.tsx | 42 +- .../fields/__snapshots__/Slider.test.tsx.snap | 828 +++++++++--------- .../settings/AppearanceSettings.tsx | 1 + .../__snapshots__/Settings.test.tsx.snap | 23 +- 4 files changed, 452 insertions(+), 442 deletions(-) diff --git a/src/components/fields/Slider.tsx b/src/components/fields/Slider.tsx index 4ee323f74..c9dd0d115 100644 --- a/src/components/fields/Slider.tsx +++ b/src/components/fields/Slider.tsx @@ -3,18 +3,19 @@ import * as SliderPrimitive from '@radix-ui/react-slider'; import { type ComponentPropsWithoutRef, type ElementRef, + type ForwardRefExoticComponent, type RefAttributes, forwardRef, } from 'react'; import { cn } from '../../utils/cn'; export type ISlider = SliderPrimitive.SliderProps & - RefAttributes; + RefAttributes & { unit?: string }; export const Slider = forwardRef< ElementRef, - ComponentPropsWithoutRef ->(({ className, min, max, step, name, ...props }, ref) => { + ComponentPropsWithoutRef> +>(({ className, min, max, step, name, unit = '', ...props }, ref) => { const range = max - min; const length = range / step + 1; return ( @@ -42,26 +43,31 @@ export const Slider = forwardRef< -
- {Array.from({ length }).map((_, i) => ( - min / step && i + 1 < (max - min) / step, - })} - role="presentation" - > - {i === 0 +
+ {Array.from({ length }).map((_, i) => { + const value = + i === 0 ? min : i * step + min === (max + min) / 2 ? (max + min) / 2 : i * step + min === max ? max - : '|'} - - ))} + : '|'; + return ( + + {value} + {value !== '|' && unit} + + ); + })}
); diff --git a/src/components/fields/__snapshots__/Slider.test.tsx.snap b/src/components/fields/__snapshots__/Slider.test.tsx.snap index c71e81d17..97a95e836 100644 --- a/src/components/fields/__snapshots__/Slider.test.tsx.snap +++ b/src/components/fields/__snapshots__/Slider.test.tsx.snap @@ -13,7 +13,7 @@ exports[`components/fields/Slider.tsx should render 1`] = ` style="--radix-slider-thumb-transform: translateX(-50%);" >
0 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 50 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 100 @@ -660,7 +660,7 @@ exports[`components/fields/Slider.tsx should render 1`] = ` style="--radix-slider-thumb-transform: translateX(-50%);" >
0 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 50 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 100 @@ -1364,14 +1364,14 @@ exports[`components/fields/Slider.tsx should render with a name 1`] = ` style="--radix-slider-thumb-transform: translateX(-50%);" >
0 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 50 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 100 @@ -2018,14 +2018,14 @@ exports[`components/fields/Slider.tsx should render with a name 1`] = ` style="--radix-slider-thumb-transform: translateX(-50%);" >
0 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 50 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 100 diff --git a/src/components/settings/AppearanceSettings.tsx b/src/components/settings/AppearanceSettings.tsx index 8f14fcae3..57f1b9f98 100644 --- a/src/components/settings/AppearanceSettings.tsx +++ b/src/components/settings/AppearanceSettings.tsx @@ -53,6 +53,7 @@ export const AppearanceSettings: FC = () => { onValueChange={(value) => { webFrame.setZoomLevel(value[0] / 100 - 1); }} + unit="%" />
50 + % | 100 + % | 150 + %
@@ -741,7 +744,7 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = ` title="app-version" > Gitify - dev + v0.0.1
From 8afd848e4a0447aee0f51c3e4993e5c62f05419b Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Mon, 1 Jul 2024 16:50:34 +0100 Subject: [PATCH 09/16] feat: add live zoom in settings page --- src/components/settings/AppearanceSettings.tsx | 14 +++++++++++--- .../__snapshots__/Settings.test.tsx.snap | 18 +++++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/components/settings/AppearanceSettings.tsx b/src/components/settings/AppearanceSettings.tsx index 57f1b9f98..a780a56e4 100644 --- a/src/components/settings/AppearanceSettings.tsx +++ b/src/components/settings/AppearanceSettings.tsx @@ -6,7 +6,7 @@ import { TagIcon, } from '@primer/octicons-react'; import { ipcRenderer, webFrame } from 'electron'; -import { type FC, useContext, useEffect } from 'react'; +import { type FC, useContext, useEffect, useState } from 'react'; import { AppContext } from '../../context/App'; import { Size, Theme } from '../../types'; import { setTheme } from '../../utils/theme'; @@ -16,6 +16,9 @@ import { Slider } from '../fields/Slider'; export const AppearanceSettings: FC = () => { const { settings, updateSetting } = useContext(AppContext); + const [zoomLevel, setZoomLevel] = useState( + webFrame.getZoomLevel() * 100 + 100, + ); useEffect(() => { ipcRenderer.on('gitify:update-theme', (_, updatedTheme: Theme) => { @@ -25,6 +28,10 @@ export const AppearanceSettings: FC = () => { }); }, [settings.theme]); + window.addEventListener('resize', () => { + setZoomLevel(webFrame.getZoomLevel() * 100 + 100); + }); + return (
@@ -45,9 +52,10 @@ export const AppearanceSettings: FC = () => { className="mb-1" /> { diff --git a/src/routes/__snapshots__/Settings.test.tsx.snap b/src/routes/__snapshots__/Settings.test.tsx.snap index 95c6a8719..62ffdb504 100644 --- a/src/routes/__snapshots__/Settings.test.tsx.snap +++ b/src/routes/__snapshots__/Settings.test.tsx.snap @@ -152,7 +152,7 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = ` - 50 + 0 % | + + | + - 100 + 75 % | + + | + Date: Tue, 2 Jul 2024 00:36:32 +0100 Subject: [PATCH 10/16] feat: implemented multiplier --- src/components/settings/AppearanceSettings.tsx | 11 ++++++----- src/routes/Settings.tsx | 2 +- src/utils/zoom.test.ts | 17 +++++++++++++++++ src/utils/zoom.ts | 15 +++++++++++++++ 4 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 src/utils/zoom.test.ts create mode 100644 src/utils/zoom.ts diff --git a/src/components/settings/AppearanceSettings.tsx b/src/components/settings/AppearanceSettings.tsx index a780a56e4..9d0a37869 100644 --- a/src/components/settings/AppearanceSettings.tsx +++ b/src/components/settings/AppearanceSettings.tsx @@ -10,6 +10,7 @@ import { type FC, useContext, useEffect, useState } from 'react'; import { AppContext } from '../../context/App'; import { Size, Theme } from '../../types'; import { setTheme } from '../../utils/theme'; +import { percentageToZoom, zoomToPercentage } from '../../utils/zoom'; import { Checkbox } from '../fields/Checkbox'; import { RadioGroup } from '../fields/RadioGroup'; import { Slider } from '../fields/Slider'; @@ -17,7 +18,7 @@ import { Slider } from '../fields/Slider'; export const AppearanceSettings: FC = () => { const { settings, updateSetting } = useContext(AppContext); const [zoomLevel, setZoomLevel] = useState( - webFrame.getZoomLevel() * 100 + 100, + zoomToPercentage(webFrame.getZoomLevel()), ); useEffect(() => { @@ -29,7 +30,7 @@ export const AppearanceSettings: FC = () => { }, [settings.theme]); window.addEventListener('resize', () => { - setZoomLevel(webFrame.getZoomLevel() * 100 + 100); + setZoomLevel(zoomToPercentage(webFrame.getZoomLevel())); }); return ( @@ -58,9 +59,9 @@ export const AppearanceSettings: FC = () => { min={0} step={25} name="Zoom" - onValueChange={(value) => { - webFrame.setZoomLevel(value[0] / 100 - 1); - }} + onValueChange={(value) => + webFrame.setZoomLevel(percentageToZoom(value[0])) + } unit="%" /> { return (
-
Settings
+
Settings
diff --git a/src/utils/zoom.test.ts b/src/utils/zoom.test.ts new file mode 100644 index 000000000..87734d6b5 --- /dev/null +++ b/src/utils/zoom.test.ts @@ -0,0 +1,17 @@ +import { percentageToZoom, zoomToPercentage } from './zoom'; + +describe('utils/zoom.ts', () => { + it('should convert percentage to zoom level', () => { + expect(percentageToZoom(100)).toBe(0); + expect(percentageToZoom(50)).toBe(-0.925); + expect(percentageToZoom(0)).toBe(-1.85); + expect(percentageToZoom(150)).toBe(0.925); + }); + + it('should convert zoom level to percentage', () => { + expect(zoomToPercentage(0)).toBe(100); + expect(zoomToPercentage(-1)).toBe(46); + expect(zoomToPercentage(-2)).toBe(-8); + expect(zoomToPercentage(1)).toBe(154); + }); +}); diff --git a/src/utils/zoom.ts b/src/utils/zoom.ts new file mode 100644 index 000000000..13dee7501 --- /dev/null +++ b/src/utils/zoom.ts @@ -0,0 +1,15 @@ +const RECOMMENDED = 100; +const MULTIPLIER = 1.85; + +/** + * Percentage to zoom level. 100% is the recommended zoom level (0). + * @param percentage 0-150 + * @returns zoomLevel -2 to 0.5 + */ +export const percentageToZoom = (percentage: number): number => { + return ((percentage - RECOMMENDED) * MULTIPLIER) / 100; +}; + +export const zoomToPercentage = (zoom: number): number => { + return Math.round((zoom / MULTIPLIER) * 100 + RECOMMENDED); +}; From c86e7fde8c8c1e0fd05c7be489cdb0b7be65e4c9 Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Tue, 2 Jul 2024 00:42:48 +0100 Subject: [PATCH 11/16] chore: set multiplier to `2` and max zoom to 120% --- src/components/settings/AppearanceSettings.tsx | 4 ++-- src/routes/__snapshots__/Settings.test.tsx.snap | 6 +++--- src/utils/zoom.test.ts | 12 ++++++------ src/utils/zoom.ts | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/components/settings/AppearanceSettings.tsx b/src/components/settings/AppearanceSettings.tsx index 9d0a37869..fefc99623 100644 --- a/src/components/settings/AppearanceSettings.tsx +++ b/src/components/settings/AppearanceSettings.tsx @@ -55,9 +55,9 @@ export const AppearanceSettings: FC = () => { webFrame.setZoomLevel(percentageToZoom(value[0])) diff --git a/src/routes/__snapshots__/Settings.test.tsx.snap b/src/routes/__snapshots__/Settings.test.tsx.snap index 62ffdb504..08fc3c27c 100644 --- a/src/routes/__snapshots__/Settings.test.tsx.snap +++ b/src/routes/__snapshots__/Settings.test.tsx.snap @@ -151,7 +151,7 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = ` > - 75 + 60 % - 150 + 120 %
diff --git a/src/utils/zoom.test.ts b/src/utils/zoom.test.ts index 87734d6b5..419a6421c 100644 --- a/src/utils/zoom.test.ts +++ b/src/utils/zoom.test.ts @@ -3,15 +3,15 @@ import { percentageToZoom, zoomToPercentage } from './zoom'; describe('utils/zoom.ts', () => { it('should convert percentage to zoom level', () => { expect(percentageToZoom(100)).toBe(0); - expect(percentageToZoom(50)).toBe(-0.925); - expect(percentageToZoom(0)).toBe(-1.85); - expect(percentageToZoom(150)).toBe(0.925); + expect(percentageToZoom(50)).toBe(-1); + expect(percentageToZoom(0)).toBe(-2); + expect(percentageToZoom(150)).toBe(1); }); it('should convert zoom level to percentage', () => { expect(zoomToPercentage(0)).toBe(100); - expect(zoomToPercentage(-1)).toBe(46); - expect(zoomToPercentage(-2)).toBe(-8); - expect(zoomToPercentage(1)).toBe(154); + expect(zoomToPercentage(-1)).toBe(50); + expect(zoomToPercentage(-2)).toBe(0); + expect(zoomToPercentage(1)).toBe(150); }); }); diff --git a/src/utils/zoom.ts b/src/utils/zoom.ts index 13dee7501..81da38f75 100644 --- a/src/utils/zoom.ts +++ b/src/utils/zoom.ts @@ -1,5 +1,5 @@ const RECOMMENDED = 100; -const MULTIPLIER = 1.85; +const MULTIPLIER = 2; /** * Percentage to zoom level. 100% is the recommended zoom level (0). @@ -11,5 +11,5 @@ export const percentageToZoom = (percentage: number): number => { }; export const zoomToPercentage = (zoom: number): number => { - return Math.round((zoom / MULTIPLIER) * 100 + RECOMMENDED); + return (zoom / MULTIPLIER) * 100 + RECOMMENDED; }; From b230c08d2a379ef31c3dc8b91fe3d754a389f988 Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Tue, 2 Jul 2024 21:45:11 +0100 Subject: [PATCH 12/16] refactor: update slider component --- src/components/fields/Slider.tsx | 121 +- .../fields/__snapshots__/Slider.test.tsx.snap | 3242 +++++++++-------- .../settings/AppearanceSettings.tsx | 5 +- .../__snapshots__/Settings.test.tsx.snap | 160 +- 4 files changed, 1789 insertions(+), 1739 deletions(-) diff --git a/src/components/fields/Slider.tsx b/src/components/fields/Slider.tsx index c9dd0d115..563219e31 100644 --- a/src/components/fields/Slider.tsx +++ b/src/components/fields/Slider.tsx @@ -10,65 +10,74 @@ import { import { cn } from '../../utils/cn'; export type ISlider = SliderPrimitive.SliderProps & - RefAttributes & { unit?: string }; + RefAttributes & { unit?: string; visualSteps?: number }; export const Slider = forwardRef< ElementRef, ComponentPropsWithoutRef> ->(({ className, min, max, step, name, unit = '', ...props }, ref) => { - const range = max - min; - const length = range / step + 1; - return ( - - {name && ( -
-
- - -
- - - - - - -
-
- - 0 - % - - - | - - - | - - - 60 - % - - - | - - - | - - - 120 - % - -
-
-
From b0cf36d9da9844bb56608fa5dc3a36a024930bcd Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Fri, 5 Jul 2024 18:02:48 +0100 Subject: [PATCH 15/16] chore: undo zoom specific changes --- .vscode/settings.json | 9 ++++++++- package.json | 1 - pnpm-lock.yaml | 8 -------- src/__mocks__/electron.js | 4 ---- .../settings/AppearanceSettings.test.tsx | 2 -- src/components/settings/SettingsFooter.test.tsx | 2 -- src/routes/Settings.test.tsx | 2 -- src/utils/zoom.test.ts | 17 ----------------- src/utils/zoom.ts | 15 --------------- 9 files changed, 8 insertions(+), 52 deletions(-) delete mode 100644 src/utils/zoom.test.ts delete mode 100644 src/utils/zoom.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 66b252e97..64b9bd6f0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,12 @@ "source.organizeImports.biome": "explicit", "quickfix.biome": "explicit" }, - "editor.defaultFormatter": "biomejs.biome" + "editor.defaultFormatter": "biomejs.biome", + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "tailwindCSS.experimental.classRegex": [ + ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], + ["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] + ] } diff --git a/package.json b/package.json index b6908de22..8765fbb3c 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,6 @@ "nock": "13.5.4", "postcss": "8.4.39", "postcss-loader": "8.1.1", - "resize-observer-polyfill": "^1.5.1", "rimraf": "5.0.7", "style-loader": "4.0.0", "tailwindcss": "3.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cb8593ad0..518cf5c50 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,9 +117,6 @@ importers: postcss-loader: specifier: 8.1.1 version: 8.1.1(postcss@8.4.39)(typescript@5.5.2)(webpack@5.92.1(webpack-cli@5.1.4)) - resize-observer-polyfill: - specifier: ^1.5.1 - version: 1.5.1 rimraf: specifier: 5.0.7 version: 5.0.7 @@ -2598,9 +2595,6 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - resize-observer-polyfill@1.5.1: - resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} - resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} @@ -6074,8 +6068,6 @@ snapshots: requires-port@1.0.0: {} - resize-observer-polyfill@1.5.1: {} - resolve-alpn@1.2.1: {} resolve-cwd@3.0.0: diff --git a/src/__mocks__/electron.js b/src/__mocks__/electron.js index c2d0e68c2..3362dc13e 100644 --- a/src/__mocks__/electron.js +++ b/src/__mocks__/electron.js @@ -49,8 +49,4 @@ module.exports = { shell: { openExternal: jest.fn(), }, - webFrame: { - setZoomLevel: jest.fn(), - getZoomLevel: jest.fn(), - }, }; diff --git a/src/components/settings/AppearanceSettings.test.tsx b/src/components/settings/AppearanceSettings.test.tsx index 16df7eeb0..96cae84b7 100644 --- a/src/components/settings/AppearanceSettings.test.tsx +++ b/src/components/settings/AppearanceSettings.test.tsx @@ -4,8 +4,6 @@ import { mockAuth, mockSettings } from '../../__mocks__/state-mocks'; import { AppContext } from '../../context/App'; import { AppearanceSettings } from './AppearanceSettings'; -global.ResizeObserver = require('resize-observer-polyfill'); - describe('routes/components/AppearanceSettings.tsx', () => { const updateSetting = jest.fn(); diff --git a/src/components/settings/SettingsFooter.test.tsx b/src/components/settings/SettingsFooter.test.tsx index 57755ddac..6074d7709 100644 --- a/src/components/settings/SettingsFooter.test.tsx +++ b/src/components/settings/SettingsFooter.test.tsx @@ -11,8 +11,6 @@ jest.mock('react-router-dom', () => ({ useNavigate: () => mockNavigate, })); -global.ResizeObserver = require('resize-observer-polyfill'); - describe('routes/components/SettingsFooter.tsx', () => { afterEach(() => { jest.clearAllMocks(); diff --git a/src/routes/Settings.test.tsx b/src/routes/Settings.test.tsx index d1f4ff3fb..962984fa6 100644 --- a/src/routes/Settings.test.tsx +++ b/src/routes/Settings.test.tsx @@ -5,8 +5,6 @@ import { mockPlatform } from '../__mocks__/utils'; import { AppContext } from '../context/App'; import { SettingsRoute } from './Settings'; -global.ResizeObserver = require('resize-observer-polyfill'); - const mockNavigate = jest.fn(); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), diff --git a/src/utils/zoom.test.ts b/src/utils/zoom.test.ts deleted file mode 100644 index 419a6421c..000000000 --- a/src/utils/zoom.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { percentageToZoom, zoomToPercentage } from './zoom'; - -describe('utils/zoom.ts', () => { - it('should convert percentage to zoom level', () => { - expect(percentageToZoom(100)).toBe(0); - expect(percentageToZoom(50)).toBe(-1); - expect(percentageToZoom(0)).toBe(-2); - expect(percentageToZoom(150)).toBe(1); - }); - - it('should convert zoom level to percentage', () => { - expect(zoomToPercentage(0)).toBe(100); - expect(zoomToPercentage(-1)).toBe(50); - expect(zoomToPercentage(-2)).toBe(0); - expect(zoomToPercentage(1)).toBe(150); - }); -}); diff --git a/src/utils/zoom.ts b/src/utils/zoom.ts deleted file mode 100644 index 81da38f75..000000000 --- a/src/utils/zoom.ts +++ /dev/null @@ -1,15 +0,0 @@ -const RECOMMENDED = 100; -const MULTIPLIER = 2; - -/** - * Percentage to zoom level. 100% is the recommended zoom level (0). - * @param percentage 0-150 - * @returns zoomLevel -2 to 0.5 - */ -export const percentageToZoom = (percentage: number): number => { - return ((percentage - RECOMMENDED) * MULTIPLIER) / 100; -}; - -export const zoomToPercentage = (zoom: number): number => { - return (zoom / MULTIPLIER) * 100 + RECOMMENDED; -}; From 6664dd01f0edc115816ade3510b6a0e761931a07 Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Fri, 5 Jul 2024 18:32:46 +0100 Subject: [PATCH 16/16] fix: button in light mode --- src/components/buttons/Button.tsx | 10 +++++----- .../buttons/__snapshots__/Button.test.tsx.snap | 8 ++++---- src/routes/__snapshots__/Login.test.tsx.snap | 8 ++++---- .../__snapshots__/LoginWithOAuthApp.test.tsx.snap | 12 ++++++------ .../LoginWithPersonalAccessToken.test.tsx.snap | 12 ++++++------ 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/components/buttons/Button.tsx b/src/components/buttons/Button.tsx index 94c08ddc1..fa3c6bd98 100644 --- a/src/components/buttons/Button.tsx +++ b/src/components/buttons/Button.tsx @@ -10,17 +10,17 @@ const buttonVariants = cva( { variants: { variant: { - default: 'bg-gray-300 text-white dark:text-black hover:opacity-90', + default: 'bg-gray-300 dark:text-black hover:opacity-90', destructive: 'bg-red-600 text-white hover:opacity-90', outline: - 'border-zinc-300 hover:text-white border bg-transparent hover:bg-zinc-100 dark:hover:bg-zinc-600', - ghost: 'hover:bg-zinc-100 dark:hover:bg-zinc-600 hover:text-white', + 'border-zinc-300 hover:text-inherit dark:hover:text-white border bg-transparent hover:bg-zinc-100 dark:hover:bg-zinc-600', + ghost: 'hover:bg-zinc-100 dark:hover:bg-zinc-600 dark:hover:text-white', link: 'underline-offset-2 hover:underline', }, size: { - default: 'min-w-20 h-10 px-2 py-1', + default: 'min-w-20 h-10 px-4 py-1', xs: 'h-6 rounded-md px-2', - sm: 'h-9 rounded-md px-2', + sm: 'h-9 rounded-md px-2 py-1', lg: 'h-11 rounded-md px-8', icon: 'h-10 w-10', }, diff --git a/src/components/buttons/__snapshots__/Button.test.tsx.snap b/src/components/buttons/__snapshots__/Button.test.tsx.snap index c2929be61..e1fb0dfd3 100644 --- a/src/components/buttons/__snapshots__/Button.test.tsx.snap +++ b/src/components/buttons/__snapshots__/Button.test.tsx.snap @@ -7,7 +7,7 @@ exports[`components/buttons/Button.tsx should render with icon 1`] = `
diff --git a/src/routes/__snapshots__/Login.test.tsx.snap b/src/routes/__snapshots__/Login.test.tsx.snap index 6289b1536..d40784a56 100644 --- a/src/routes/__snapshots__/Login.test.tsx.snap +++ b/src/routes/__snapshots__/Login.test.tsx.snap @@ -66,7 +66,7 @@ exports[`routes/Login.tsx should render itself & its children 1`] = `