Skip to content

Commit 853531a

Browse files
committed
Refactoring and unit testing implemented
1 parent e3b7e3d commit 853531a

File tree

12 files changed

+185
-93
lines changed

12 files changed

+185
-93
lines changed

packages/api-explorer/src/ApiExplorer.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ import {
6868
selectSpecs,
6969
selectCurrentSpec,
7070
selectSdkLanguage,
71+
selectTagFilter,
7172
} from './state'
7273
import {
7374
getSpecKey,
@@ -99,6 +100,7 @@ export const ApiExplorer: FC<ApiExplorerProps> = ({
99100
const specs = useSelector(selectSpecs)
100101
const spec = useSelector(selectCurrentSpec)
101102
const selectedSdkLanguage = useSelector(selectSdkLanguage)
103+
const selectedTagFilter = useSelector(selectTagFilter)
102104
const { initLodesAction } = useLodeActions()
103105
const {
104106
initSettingsAction,
@@ -136,10 +138,13 @@ export const ApiExplorer: FC<ApiExplorerProps> = ({
136138
// reconcile local storage state with URL or vice versa
137139
if (initialized) {
138140
const sdkParam = searchParams.get('sdk') || ''
141+
const verbParam = searchParams.get('v') || ''
139142
const sdk = findSdk(sdkParam)
140143
const validSdkParam =
141144
!sdkParam.localeCompare(sdk.alias, 'en', { sensitivity: 'base' }) ||
142145
!sdkParam.localeCompare(sdk.language, 'en', { sensitivity: 'base' })
146+
const validVerbParam = isValidFilter(location, verbParam)
147+
143148
if (validSdkParam) {
144149
// sync store with URL
145150
setSdkLanguageAction({
@@ -152,6 +157,14 @@ export const ApiExplorer: FC<ApiExplorerProps> = ({
152157
sdk: alias === allAlias ? null : alias,
153158
})
154159
}
160+
161+
if (validVerbParam) {
162+
setTagFilterAction({ tagFilter: verbParam.toUpperCase() })
163+
} else {
164+
navigate(location.pathname, {
165+
v: selectedTagFilter === 'ALL' ? null : selectedTagFilter,
166+
})
167+
}
155168
}
156169
}, [initialized])
157170

@@ -171,8 +184,6 @@ export const ApiExplorer: FC<ApiExplorerProps> = ({
171184
const { language: sdkLanguage } = findSdk(sdkParam)
172185
setSearchPatternAction({ searchPattern })
173186
setSdkLanguageAction({ sdkLanguage })
174-
// TODO: need to validate verbParam, checking against all available
175-
// httpMethod and metaType options, default to ALL if not valid
176187
setTagFilterAction({
177188
tagFilter: isValidFilter(location, verbParam)
178189
? verbParam.toUpperCase()

packages/api-explorer/src/components/SelectorContainer/SelectorContainer.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ export const SelectorContainer: FC<SelectorContainerProps> = ({
5151
...spaceProps
5252
}) => {
5353
const searchParams = new URLSearchParams(location.search)
54+
// TODO: noticing that there are certain pages where we must delete extra params
55+
// before pushing its link, what's a way we can handle this?
56+
searchParams.delete('v')
5457
return (
5558
<Space width="auto" {...spaceProps}>
5659
<SdkLanguageSelector />

packages/api-explorer/src/components/SideNav/SideNav.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export const SideNav: FC<SideNavProps> = ({ headless = false, spec }) => {
8484
if (parts[1] === 'diff') {
8585
if (parts[3] !== tabNames[index]) {
8686
parts[3] = tabNames[index]
87-
navigate(parts.join('/'))
87+
navigate(parts.join('/'), { v: null })
8888
}
8989
} else {
9090
if (parts[2] !== tabNames[index]) {

packages/api-explorer/src/components/SideNav/SideNavMethods.tsx

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { useSelector } from 'react-redux'
3232
import { useLocation, useRouteMatch } from 'react-router-dom'
3333
import { useNavigation, highlightHTML, buildMethodPath } from '../../utils'
3434
import { Link } from '../Link'
35-
import { selectSearchPattern, selectTagFilter } from '../../state'
35+
import { selectSearchPattern } from '../../state'
3636

3737
interface MethodsProps {
3838
methods: MethodList
@@ -48,7 +48,6 @@ export const SideNavMethods = styled(
4848
const navigate = useNavigation()
4949
const searchParams = new URLSearchParams(location.search)
5050
const searchPattern = useSelector(selectSearchPattern)
51-
const selectedTagFilter = useSelector(selectTagFilter)
5251
const match = useRouteMatch<{ methodTag: string }>(
5352
`/:specKey/methods/:methodTag/:methodName?`
5453
)
@@ -83,24 +82,23 @@ export const SideNavMethods = styled(
8382
}
8483
>
8584
<ul>
86-
{Object.values(methods).map(
87-
(method) =>
88-
(selectedTagFilter === 'ALL' ||
89-
selectedTagFilter === method.httpMethod) && (
90-
<li key={method.name}>
91-
<Link
92-
to={buildMethodPath(
93-
specKey,
94-
tag,
95-
method.name,
96-
searchParams.toString()
97-
)}
98-
>
99-
{highlightHTML(searchPattern, method.summary)}
100-
</Link>
101-
</li>
102-
)
103-
)}
85+
{Object.values(methods).map((method) => (
86+
<li key={method.name}>
87+
<Link
88+
to={() => {
89+
searchParams.delete('v')
90+
return buildMethodPath(
91+
specKey,
92+
tag,
93+
method.name,
94+
searchParams.toString()
95+
)
96+
}}
97+
>
98+
{highlightHTML(searchPattern, method.summary)}
99+
</Link>
100+
</li>
101+
))}
104102
</ul>
105103
</Accordion2>
106104
)

packages/api-explorer/src/components/SideNav/SideNavTypes.tsx

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { useLocation, useRouteMatch } from 'react-router-dom'
3232
import { useSelector } from 'react-redux'
3333
import { Link } from '../Link'
3434
import { highlightHTML, useNavigation, buildTypePath } from '../../utils'
35-
import { selectSearchPattern, selectTagFilter } from '../../state'
35+
import { selectSearchPattern } from '../../state'
3636

3737
interface TypesProps {
3838
types: TypeList
@@ -48,7 +48,6 @@ export const SideNavTypes = styled(
4848
const navigate = useNavigation()
4949
const searchParams = new URLSearchParams(location.search)
5050
const searchPattern = useSelector(selectSearchPattern)
51-
const selectedTagFilter = useSelector(selectTagFilter)
5251
const match = useRouteMatch<{ typeTag: string }>(
5352
`/:specKey/types/:typeTag/:typeName?`
5453
)
@@ -83,24 +82,23 @@ export const SideNavTypes = styled(
8382
}
8483
>
8584
<ul>
86-
{Object.values(types).map(
87-
(type) =>
88-
(selectedTagFilter === 'ALL' ||
89-
selectedTagFilter === type.metaType.toUpperCase()) && (
90-
<li key={type.name}>
91-
<Link
92-
to={`${buildTypePath(
93-
specKey,
94-
tag,
95-
type.name,
96-
searchParams.toString()
97-
)}`}
98-
>
99-
{highlightHTML(searchPattern, type.name)}
100-
</Link>
101-
</li>
102-
)
103-
)}
85+
{Object.values(types).map((type) => (
86+
<li key={type.name}>
87+
<Link
88+
to={() => {
89+
searchParams.delete('v')
90+
return buildTypePath(
91+
specKey,
92+
tag,
93+
type.name,
94+
searchParams.toString()
95+
)
96+
}}
97+
>
98+
{highlightHTML(searchPattern, type.name)}
99+
</Link>
100+
</li>
101+
))}
104102
</ul>
105103
</Accordion2>
106104
)

packages/api-explorer/src/scenes/MethodTagScene/MethodTagScene.spec.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import { api } from '../../test-data'
3232
import { renderWithRouterAndReduxProvider } from '../../test-utils'
3333
import { MethodTagScene } from './MethodTagScene'
3434

35-
// TODO: use constant from path.ts
3635
const opBtnNames = /ALL|GET|POST|PUT|PATCH|DELETE/
3736

3837
const mockHistoryPush = jest.fn()

packages/api-explorer/src/scenes/MethodTagScene/MethodTagScene.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,15 @@ export const MethodTagScene: FC<MethodTagSceneProps> = ({ api }) => {
9999
selectedTagFilter === method.httpMethod) && (
100100
<Link
101101
key={index}
102-
to={buildMethodPath(
103-
specKey,
104-
tag.name,
105-
method.name,
106-
searchParams.toString()
107-
)}
102+
to={() => {
103+
searchParams.delete('v')
104+
return buildMethodPath(
105+
specKey,
106+
tag.name,
107+
method.name,
108+
searchParams.toString()
109+
)
110+
}}
108111
>
109112
<Grid columns={1} py="xsmall">
110113
<DocMethodSummary key={index} method={method} />

packages/api-explorer/src/scenes/TypeTagScene/TypeTagScene.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,15 @@ export const TypeTagScene: FC<TypeTagSceneProps> = ({ api }) => {
9696
selectedTagFilter === type.metaType.toString().toUpperCase()) && (
9797
<Link
9898
key={index}
99-
to={buildTypePath(
100-
specKey,
101-
tag.name,
102-
type.name,
103-
searchParams.toString()
104-
)}
99+
to={() => {
100+
searchParams.delete('v')
101+
return buildTypePath(
102+
specKey,
103+
tag.name,
104+
type.name,
105+
searchParams.toString()
106+
)
107+
}}
105108
>
106109
<Grid columns={1} py="xsmall">
107110
<DocTypeSummary key={index} type={type} />

packages/api-explorer/src/test-data/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ export * from './specs'
2727
export { examples } from './examples'
2828
export * from './declarations'
2929
export * from './sdkLanguages'
30+
export * from './tagFilters'
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
3+
MIT License
4+
5+
Copyright (c) 2022 Looker Data Sciences, Inc.
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.
24+
25+
*/
26+
27+
export const methodFilters = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']
28+
export const typeFilters = ['SPECIFICATION', 'WRITE', 'REQUEST', 'ENUMERATED']

packages/api-explorer/src/utils/path.spec.ts

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,14 @@
2424
2525
*/
2626

27-
import { api } from '../test-data'
28-
import { buildMethodPath, buildPath, buildTypePath } from './path'
27+
import { api, methodFilters, typeFilters } from '../test-data'
28+
import {
29+
buildMethodPath,
30+
buildPath,
31+
buildTypePath,
32+
getSceneType,
33+
isValidFilter,
34+
} from './path'
2935

3036
describe('path utils', () => {
3137
const testParam = 's=test'
@@ -74,4 +80,47 @@ describe('path utils', () => {
7480
expect(path).toEqual('/3.1/types/Dashboard/Dashboard')
7581
})
7682
})
83+
84+
describe('getSceneType', () => {
85+
test('returns correct scene type given location with pathname', () => {
86+
const methodLocation = {
87+
pathname: '/3.1/methods/RandomMethod',
88+
} as Location
89+
const typeLocation = { pathname: '/3.1/types/RandomType' } as Location
90+
expect(getSceneType(methodLocation)).toEqual('methods')
91+
expect(getSceneType(typeLocation)).toEqual('types')
92+
})
93+
test('returns empty string if there is no scene type', () => {
94+
const noSceneTypePath = { pathname: '/' } as Location
95+
expect(getSceneType(noSceneTypePath)).toEqual('')
96+
})
97+
})
98+
99+
describe('isValidFilter', () => {
100+
const methodLocation = {
101+
pathname: '/3.1/methods/RandomMethod',
102+
} as Location
103+
const typeLocation = { pathname: '/3.1/types/RandomType' } as Location
104+
105+
test("validates 'all' as a valid filter for methods and types", () => {
106+
expect(isValidFilter(methodLocation, 'ALL')).toBe(true)
107+
expect(isValidFilter(typeLocation, 'ALL')).toBe(true)
108+
})
109+
110+
test.each(methodFilters)(
111+
'validates %s as a valid method filter',
112+
(filter) => {
113+
expect(isValidFilter(methodLocation, filter)).toBe(true)
114+
}
115+
)
116+
117+
test('invalidates wrong parameter for methods and types', () => {
118+
expect(isValidFilter(methodLocation, 'INVALID')).toBe(false)
119+
expect(isValidFilter(typeLocation, 'INVALID')).toBe(false)
120+
})
121+
122+
test.each(typeFilters)('validates %s as a valid type filter', (filter) => {
123+
expect(isValidFilter(typeLocation, filter)).toBe(true)
124+
})
125+
})
77126
})

0 commit comments

Comments
 (0)