Skip to content

Commit 028c89c

Browse files
Merge branch 'main' into cy/org_page_ui_improvements
2 parents 133554a + de8f585 commit 028c89c

File tree

97 files changed

+942
-752
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+942
-752
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
},
3232
"dependencies": {
3333
"@amplitude/analytics-browser": "^2.11.9",
34+
"@amplitude/analytics-types": "^2.8.4",
3435
"@hookform/resolvers": "^2.8.5",
3536
"@radix-ui/react-accordion": "^1.1.2",
3637
"@radix-ui/react-checkbox": "^1.1.1",
@@ -83,7 +84,6 @@
8384
"react-router-dom-v5-compat": "^6.15.0",
8485
"react-use": "^17.2.4",
8586
"recharts": "^2.12.7",
86-
"semver": "7.5.2",
8787
"tailwind-merge": "^2.3.0",
8888
"zod": "^3.21.4"
8989
},

public/logo_dark.svg

Lines changed: 16 additions & 0 deletions
Loading

src/index.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,30 @@ if (
3030

3131
ReactModal.setAppElement('#root')
3232

33-
// use with pattern to not block app loading.
34-
const FeatureFlagApp = withFeatureFlagProvider(App)
35-
36-
const ProfiledApp = Sentry.withProfiler(FeatureFlagApp)
37-
3833
const history = createBrowserHistory()
3934

4035
const TOO_MANY_REQUESTS_ERROR_CODE = 429
4136

4237
initEventTracker()
4338
setupSentry({ history })
4439

40+
// use with pattern to not block app loading.
41+
const FeatureFlagApp = withFeatureFlagProvider(App)
42+
43+
const ProfiledApp = Sentry.withProfiler(FeatureFlagApp)
44+
45+
// setting to 2 minutes, this value will ensure that components that are mounted
46+
// after suspense do not trigger a new query to be fetched. By default, the
47+
// stale time value is 0, which means that the query will be re-fetched on every
48+
// mount.
49+
const QUERY_STALE_TIME = 2 * (1000 * 60)
50+
4551
const queryClient = new QueryClient({
4652
defaultOptions: {
4753
queries: {
4854
suspense: true,
55+
staleTime: QUERY_STALE_TIME,
56+
refetchOnWindowFocus: false,
4957
retry: (failureCount, error) => {
5058
// Do not retry if the response status is 429
5159
if (
@@ -59,14 +67,15 @@ const queryClient = new QueryClient({
5967
// Otherwise, retry up to 3 times
6068
return failureCount < 3
6169
},
62-
refetchOnWindowFocus: false,
6370
},
6471
},
6572
})
6673

6774
const queryClientV5 = new QueryClientV5({
6875
defaultOptions: {
6976
queries: {
77+
staleTime: QUERY_STALE_TIME,
78+
refetchOnWindowFocus: false,
7079
retry: (failureCount, error) => {
7180
// Do not retry if the response status is 429
7281
if (
@@ -80,7 +89,6 @@ const queryClientV5 = new QueryClientV5({
8089
// Otherwise, retry up to 3 times
8190
return failureCount < 3
8291
},
83-
refetchOnWindowFocus: false,
8492
},
8593
},
8694
})

src/layouts/BaseLayout/BaseLayout.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useQuery as useQueryV5 } from '@tanstack/react-queryV5'
2-
import { lazy, Suspense } from 'react'
2+
import { Suspense } from 'react'
33
import { Redirect, useParams } from 'react-router-dom'
44

55
import Footer from 'layouts/Footer'
@@ -11,6 +11,7 @@ import SilentNetworkErrorWrapper from 'layouts/shared/SilentNetworkErrorWrapper'
1111
import ToastNotifications from 'layouts/ToastNotifications'
1212
import { OnboardingContainerProvider } from 'pages/OwnerPage/OnboardingContainerContext/context'
1313
import { RepoBreadcrumbProvider } from 'pages/RepoPage/context'
14+
import TermsOfService from 'pages/TermsOfService'
1415
import { useEventContext } from 'services/events/hooks'
1516
import { useImpersonate } from 'services/impersonate'
1617
import { useTracking } from 'services/tracking'
@@ -21,8 +22,6 @@ import LoadingLogo from 'ui/LoadingLogo'
2122
import { NavigatorDataQueryOpts } from './hooks/NavigatorDataQueryOpts'
2223
import { useUserAccessGate } from './hooks/useUserAccessGate'
2324

24-
const TermsOfService = lazy(() => import('pages/TermsOfService'))
25-
2625
const FullPageLoader = () => (
2726
<div className="mt-16 flex flex-1 items-center justify-center">
2827
<LoadingLogo />

src/layouts/BaseLayout/hooks/useUserAccessGate.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,11 @@ const useUserAccessGate = () => {
5959
})
6060

6161
useEffect(() => {
62-
if (!userData?.owner?.defaultOrgUsername) {
62+
// only update the default org if the user exists
63+
if (userData && !userData?.owner?.defaultOrgUsername) {
6364
updateDefaultOrg({ username: userData?.user?.username })
6465
}
65-
}, [
66-
userData?.user?.username,
67-
userData?.owner?.defaultOrgUsername,
68-
updateDefaultOrg,
69-
])
66+
}, [userData, updateDefaultOrg])
7067

7168
useOnboardingRedirect({
7269
username: userData?.user?.username,

src/pages/AccountSettings/tabs/Access/CreateTokenModal.jsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,25 @@ import PropTypes from 'prop-types'
22
import { useState } from 'react'
33
import { useForm } from 'react-hook-form'
44

5-
import { useGenerateUserToken } from 'services/access'
5+
import { useGenerateUserToken } from 'services/access/useGenerateUserToken'
66
import Button from 'ui/Button'
77
import { CopyClipboard } from 'ui/CopyClipboard'
88
import Modal from 'ui/Modal'
99
import TextInput from 'ui/TextInput/TextInput'
1010

1111
function CreateTokenModal({ closeModal, provider }) {
12+
const [token, setToken] = useState(null)
1213
const { register, handleSubmit, watch } = useForm({
13-
defaultValues: {
14-
name: '',
15-
},
14+
defaultValues: { name: '' },
1615
})
1716
const nameValue = watch('name', '')
18-
19-
const [token, setToken] = useState(null)
20-
2117
const { mutate, isLoading } = useGenerateUserToken({ provider })
2218

2319
const submit = ({ name }) => {
2420
mutate(
2521
{ name },
2622
{
27-
onSuccess: ({ data }) => {
23+
onSuccess: (data) => {
2824
setToken(data?.createUserToken?.fullToken)
2925
},
3026
}

src/pages/AccountSettings/tabs/Access/CreateTokenModal.test.jsx

Lines changed: 70 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,55 @@
1-
import { render, screen, waitFor } from 'custom-testing-library'
2-
1+
import {
2+
QueryClientProvider as QueryClientProviderV5,
3+
QueryClient as QueryClientV5,
4+
} from '@tanstack/react-queryV5'
5+
import { render, screen, waitFor } from '@testing-library/react'
36
import userEvent from '@testing-library/user-event'
4-
5-
import { useGenerateUserToken } from 'services/access'
7+
import { graphql, HttpResponse } from 'msw'
8+
import { setupServer } from 'msw/node'
69

710
import CreateTokenModal from './CreateTokenModal'
811

9-
vi.mock('services/access')
12+
const queryClientV5 = new QueryClientV5({
13+
defaultOptions: { queries: { retry: false } },
14+
})
15+
16+
const wrapper = ({ children }) => (
17+
<QueryClientProviderV5 client={queryClientV5}>
18+
{children}
19+
</QueryClientProviderV5>
20+
)
21+
22+
const server = setupServer()
23+
24+
beforeAll(() => {
25+
server.listen()
26+
})
27+
28+
beforeEach(() => {
29+
queryClientV5.clear()
30+
server.resetHandlers()
31+
})
32+
33+
afterAll(() => {
34+
server.close()
35+
})
1036

1137
describe('CreateTokenModal', () => {
1238
function setup() {
1339
const user = userEvent.setup()
1440
const closeModal = vi.fn()
15-
const success = {
16-
data: {
17-
createUserToken: {
18-
fullToken: '111-222-333',
19-
},
20-
},
21-
}
22-
const mutate = vi.fn((_, { onSuccess }) => {
23-
return onSuccess(success)
24-
})
25-
useGenerateUserToken.mockReturnValue({
26-
mutate,
27-
})
41+
const mutateMock = vi.fn()
42+
43+
server.use(
44+
graphql.mutation('CreateUserToken', (info) => {
45+
mutateMock(info.variables)
46+
return HttpResponse.json({
47+
data: { createUserToken: { fullToken: '111-222-333', error: null } },
48+
})
49+
})
50+
)
2851

29-
return { mutate, closeModal, user }
52+
return { mutateMock, closeModal, user }
3053
}
3154

3255
describe('renders initial CreateTokenModal', () => {
@@ -38,7 +61,8 @@ describe('CreateTokenModal', () => {
3861
provider="gh"
3962
showModal={true}
4063
closeModal={closeModal}
41-
/>
64+
/>,
65+
{ wrapper }
4266
)
4367

4468
const title = screen.getByText(/Generate new API access token/)
@@ -52,7 +76,8 @@ describe('CreateTokenModal', () => {
5276
provider="gh"
5377
showModal={true}
5478
closeModal={closeModal}
55-
/>
79+
/>,
80+
{ wrapper }
5681
)
5782

5883
const label = screen.getByText(/Token Name/)
@@ -68,7 +93,8 @@ describe('CreateTokenModal', () => {
6893
provider="gh"
6994
showModal={true}
7095
closeModal={closeModal}
71-
/>
96+
/>,
97+
{ wrapper }
7298
)
7399

74100
const buttons = screen.getAllByRole('button')
@@ -78,21 +104,25 @@ describe('CreateTokenModal', () => {
78104

79105
describe('when the user types a token name and submits', () => {
80106
it('calls the mutation', async () => {
81-
const { mutate, closeModal, user } = setup()
107+
const { mutateMock, closeModal, user } = setup()
82108
render(
83109
<CreateTokenModal
84110
provider="gh"
85111
showModal={true}
86112
closeModal={closeModal}
87-
/>
113+
/>,
114+
{ wrapper }
88115
)
89116

90117
const input = screen.getByRole('textbox')
91118
await user.type(input, '2333')
92119
const generateToken = screen.getByText('Generate Token')
93120
await user.click(generateToken)
94121

95-
expect(mutate).toHaveBeenCalled()
122+
await waitFor(() => expect(mutateMock).toHaveBeenCalled())
123+
expect(mutateMock).toHaveBeenCalledWith({
124+
input: { name: '2333', tokenType: 'api' },
125+
})
96126
})
97127

98128
describe('when mutation is successful', () => {
@@ -103,7 +133,8 @@ describe('CreateTokenModal', () => {
103133
provider="gh"
104134
showModal={true}
105135
closeModal={closeModal}
106-
/>
136+
/>,
137+
{ wrapper }
107138
)
108139

109140
const title = await screen.findByText(/API access token/)
@@ -117,7 +148,8 @@ describe('CreateTokenModal', () => {
117148
provider="gh"
118149
showModal={true}
119150
closeModal={closeModal}
120-
/>
151+
/>,
152+
{ wrapper }
121153
)
122154

123155
const input = screen.getByRole('textbox')
@@ -136,41 +168,48 @@ describe('CreateTokenModal', () => {
136168
const warning = screen.getByText(/Make sure to copy your token now/)
137169
expect(warning).toBeInTheDocument()
138170
})
171+
139172
it('renders footer', async () => {
140173
const { closeModal, user } = setup()
141174
render(
142175
<CreateTokenModal
143176
provider="gh"
144177
showModal={true}
145178
closeModal={closeModal}
146-
/>
179+
/>,
180+
{ wrapper }
147181
)
148182

149183
const input = screen.getByRole('textbox')
150184
await user.type(input, '2333')
185+
151186
const generateToken = screen.getByText('Generate Token')
152187
await user.click(generateToken)
153188

154-
const button = screen.getByRole('button', {
189+
const button = await screen.findByRole('button', {
155190
name: /done/i,
156191
})
157192
expect(button).toBeInTheDocument()
158193
})
194+
159195
it('close modals', async () => {
160196
const { closeModal, user } = setup()
161197
render(
162198
<CreateTokenModal
163199
provider="gh"
164200
showModal={true}
165201
closeModal={closeModal}
166-
/>
202+
/>,
203+
{ wrapper }
167204
)
168205

169206
const input = screen.getByRole('textbox')
170207
await user.type(input, '2333')
208+
171209
const generateToken = screen.getByText('Generate Token')
172210
await user.click(generateToken)
173-
const done = screen.getByText('Done')
211+
212+
const done = await screen.findByText('Done')
174213
await user.click(done)
175214

176215
await waitFor(() => {

0 commit comments

Comments
 (0)