Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 🪲 Debug dialog #343

Merged
merged 1 commit into from
Aug 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/bug.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,10 @@ assignees: MorganeLecurieux
**Screenshots**
<!--- If applicable, add screenshots to help explain your problem. -->

**Environment infos**
<!--- Please go to the running app and use the Ctrl + I shortcut
Then copy/past the given infos here
-->

**Additional context**
<!--- Add any other context about the problem here. -->
9 changes: 9 additions & 0 deletions ditto/base.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
{
"text_62f50d3cc15266f3bd1d83ce": "Infos copy to clipboard",
"text_62f50d26c989ab0319688498": "Environment information",
"text_62f50d26c989ab031968849a": "Use and share those information if you’re <a rel=\"external\" target=\"_blank\" href=\"https://github.com/getlago/lago-front/issues/new?labels=%F0%9F%90%9E+bug&template=bug.md&title=%5BBUG%5D\">reporting a bug to Lago</a>",
"text_62f50d26c989ab031968849e": "App environment ",
"text_62f50d26c989ab03196884a2": "API URL",
"text_62f50d26c989ab03196884a6": "User ID",
"text_62f50d26c989ab03196884aa": "App version",
"text_62f50d26c989ab03196884ac": "Copy all info",
"text_62f50d26c989ab03196884ae": "Close",
"text_62c6c95fe73d08be5b86c334": "Version",
"text_6295e58352f39200d902b01c": "Documentation",
"text_6295e58352f39200d902b02a": "Apply add-on",
Expand Down
2 changes: 2 additions & 0 deletions ditto/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,7 @@ projects:
id: 62c6c95d9333c1ac1be9f938
- name: 'Plans,Customers - Add multiple plans to a customer'
id: 62d7f6138ca07e88551c9dde
- name: '⚙️ [WIP] - General - FE environment infos'
id: 62f50d2512182c6df6c39e37
format: flat
variants: true
3 changes: 3 additions & 0 deletions ditto/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ module.exports = {
"project_6271200612648800e9bdfd47": {
"base": require('./Settings - Webhooks in app__base.json')
},
"project_62f50d2512182c6df6c39e37": {
"base": require('./⚙️ [WIP] - General - FE environment infos__base.json')
},
"project_62c6c95d9333c1ac1be9f938": {
"base": require('./👍 [Ready for dev] - Navigation - Display app version__base.json')
}
Expand Down
13 changes: 12 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react'
import { useState, useEffect, useRef } from 'react'
import { BrowserRouter } from 'react-router-dom'
import { ThemeProvider } from '@mui/material'
import { ApolloClient, NormalizedCacheObject, ApolloProvider } from '@apollo/client'
Expand All @@ -10,9 +10,19 @@ import { ToastContainer } from '~/components/designSystem/Toasts'
import { inputGlobalStyles } from '~/styles/globalStyle'
import { ErrorBoundary } from '~/components/ErrorBoundary'
import { RouteWrapper } from '~/components/RouteWrapper'
import { useShortcuts } from '~/hooks/ui/useShortcuts'
import { DebugInfoDialog, DebugInfoDialogRef } from '~/components/DebugInfoDialog'

const App = () => {
const [client, setClient] = useState<ApolloClient<NormalizedCacheObject> | null>(null)
const debugInfoDialogRef = useRef<DebugInfoDialogRef>(null)

useShortcuts([
{
keys: ['Ctrl', 'KeyI'],
action: () => debugInfoDialogRef.current?.openDialog(),
},
])

useEffect(() => {
async function initApolloClient() {
Expand All @@ -37,6 +47,7 @@ const App = () => {
</ErrorBoundary>
<UserIdentifier />
<ToastContainer />
<DebugInfoDialog ref={debugInfoDialogRef} />
</ThemeProvider>
</ApolloProvider>
</BrowserRouter>
Expand Down
94 changes: 94 additions & 0 deletions src/components/DebugInfoDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { forwardRef } from 'react'
import styled from 'styled-components'

import { Dialog, DialogRef, Typography, Button } from '~/components/designSystem'
import { useInternationalization } from '~/hooks/core/useInternationalization'
import { envGlobalVar, useCurrentUserInfosVar, addToast } from '~/core/apolloClient'
import { theme } from '~/styles'

const { appEnv, apiUrl, appVersion } = envGlobalVar()

export interface DebugInfoDialogRef extends DialogRef {}

export const DebugInfoDialog = forwardRef<DialogRef>(({}, ref) => {
const { translate } = useInternationalization()
const { user } = useCurrentUserInfosVar()

return (
<Dialog
ref={ref}
title={translate('text_62f50d26c989ab0319688498')}
description={<Typography html={translate('text_62f50d26c989ab031968849a')} />}
actions={({ closeDialog }) => (
<>
<Button
variant="quaternary"
onClick={() => {
navigator.clipboard.writeText(
`### Environment informations
**App environment :** ${appEnv}
**API URL :** ${apiUrl}
**Version :** ${appVersion}` +
(!!user?.id
? `
**User Id :** ${user?.id}`
: '')
)

addToast({
severity: 'info',
translateKey: 'text_62f50d3cc15266f3bd1d83ce',
})
}}
>
{translate('text_62f50d26c989ab03196884ac')}
</Button>
<Button onClick={closeDialog}>{translate('text_62f50d26c989ab03196884ae')}</Button>
</>
)}
>
<Content>
<Line>
<Typography variant="caption">{translate('text_62f50d26c989ab031968849e')}</Typography>
<Typography color="textSecondary">{appEnv}</Typography>
</Line>
<Line>
<Typography variant="caption">{translate('text_62f50d26c989ab03196884a2')}</Typography>
<Typography color="textSecondary">{apiUrl}</Typography>
</Line>
<Line>
<Typography variant="caption">{translate('text_62f50d26c989ab03196884aa')}</Typography>
<Typography color="textSecondary">{appVersion}</Typography>
</Line>
{user?.id && (
<Line>
<Typography variant="caption">{translate('text_62f50d26c989ab03196884a6')}</Typography>
<Typography color="textSecondary">{user?.id}</Typography>
</Line>
)}
</Content>
</Dialog>
)
})

const Content = styled.div`
> *:not(:last-child) {
margin-bottom: ${theme.spacing(3)};
}
> *:last-child {
margin-bottom: ${theme.spacing(8)};
}
`

const Line = styled.div`
display: flex;
align-items: baseline;

> *:first-child {
width: 140px;
min-width: 140px;
margin-right: ${theme.spacing(3)};
}
`

DebugInfoDialog.displayName = 'DebugInfoDialog'
26 changes: 26 additions & 0 deletions src/hooks/ui/__tests__/useShortcuts.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,32 @@ describe('useShortcuts()', () => {
})
})

describe('when additionnal keys are pressed', () => {
let action = jest.fn()
let shortcuts: Shortcut[] = [
{
action,
keys: ['Cmd', 'KeyD'],
disabled: false,
},
]

it('should not fire the action', () => {
render(
<div>
<MyTestComponentThatUsesShortcuts shortcuts={shortcuts} />
</div>
)

// cf: https://testing-library.com/docs/ecosystem-user-event#keyboardtext-options
userEvent.keyboard('{Meta}')
userEvent.keyboard('I')
userEvent.keyboard('D')

expect(action).not.toHaveBeenCalled()
})
})

describe('getCleanKey()', () => {
it('should clean keys correctly', () => {
expect(getCleanKey('MetaLeft')).toEqual('Cmd')
Expand Down
55 changes: 23 additions & 32 deletions src/hooks/ui/useShortcuts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ type CleanedShortcut = {
action: () => void
}

type ReducedShortcut = {
usableShortcuts: Record<string, CleanedShortcut>
usableKeys: string[]
}
type ReducedShortcut = Record<string, CleanedShortcut>

export const getCleanKey = (key: string) => {
switch (key) {
Expand Down Expand Up @@ -65,47 +62,41 @@ type UseShortcutReturn = (shortcuts: Shortcut[]) => { isMac: boolean }
export const useShortcuts: UseShortcutReturn = (shortcuts) => {
const isMac = navigator.platform.toUpperCase().includes('MAC')
const keyPressedRef = useRef<Record<string, boolean>>({})
const { usableShortcuts, usableKeys } = useMemo(
const usableShortcuts = useMemo(
() =>
shortcuts.reduce<ReducedShortcut>(
(acc, shortcut) => {
if (shortcut.disabled) return acc
// Get keys according to OS
const keys = (
!!shortcut?.windowsKeys && !isMac ? shortcut?.windowsKeys : shortcut?.keys
).map((key) => getCleanKey(key))
const shortcutId = getShortcutId(keys)

acc.usableShortcuts[shortcutId] = { keys, action: shortcut.action }
acc.usableKeys = [...acc.usableKeys, ...keys]

return acc
},
{ usableShortcuts: {}, usableKeys: [] }
),
shortcuts.reduce<ReducedShortcut>((acc, shortcut) => {
if (shortcut.disabled) return acc
// Get keys according to OS
const keys = (
!!shortcut?.windowsKeys && !isMac ? shortcut?.windowsKeys : shortcut?.keys
).map((key) => getCleanKey(key))
const shortcutId = getShortcutId(keys)

acc[shortcutId] = { keys, action: shortcut.action }

return acc
}, {}),
[shortcuts, isMac]
)

const onKeyDown: (e: Event) => void = useCallback(
(e) => {
const cleanKey = getCleanKey((e as unknown as KeyboardEvent).code)

if (usableKeys.includes(cleanKey)) {
keyPressedRef.current[cleanKey] = true
keyPressedRef.current[cleanKey] = true

const pressKeysID = getShortcutId(
Object.keys(keyPressedRef.current).filter((key) => !!keyPressedRef.current[key])
)
const pressKeysID = getShortcutId(
Object.keys(keyPressedRef.current).filter((key) => !!keyPressedRef.current[key])
)

if (!!usableShortcuts[pressKeysID]) {
usableShortcuts[pressKeysID].action()
if (!!usableShortcuts[pressKeysID]) {
usableShortcuts[pressKeysID].action()

// Clean after use of one shortcut to it to be recalled right away
keyPressedRef.current = {}
}
// Clean after use of one shortcut to it to be recalled right away
keyPressedRef.current = {}
}
},
[usableShortcuts, usableKeys]
[usableShortcuts]
)

const onKeyUp: (e: Event) => void = useCallback((e) => {
Expand Down