@looker/api-explorer 0.9.35 → 0.9.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/dist/bundle.js +1 -1
  3. package/dist/bundle.js.map +1 -1
  4. package/lib/ApiExplorer.js +9 -1
  5. package/lib/ApiExplorer.js.map +1 -1
  6. package/lib/components/DocMarkdown/DocMarkdown.js +7 -7
  7. package/lib/components/DocMarkdown/DocMarkdown.js.map +1 -1
  8. package/lib/components/SelectorContainer/ApiSpecSelector.js +4 -2
  9. package/lib/components/SelectorContainer/ApiSpecSelector.js.map +1 -1
  10. package/lib/components/SideNav/SideNav.js +23 -13
  11. package/lib/components/SideNav/SideNav.js.map +1 -1
  12. package/lib/components/SideNav/SideNavMethods.js +8 -6
  13. package/lib/components/SideNav/SideNavMethods.js.map +1 -1
  14. package/lib/components/SideNav/SideNavTypes.js +6 -4
  15. package/lib/components/SideNav/SideNavTypes.js.map +1 -1
  16. package/lib/esm/ApiExplorer.js +9 -1
  17. package/lib/esm/ApiExplorer.js.map +1 -1
  18. package/lib/esm/components/DocMarkdown/DocMarkdown.js +4 -4
  19. package/lib/esm/components/DocMarkdown/DocMarkdown.js.map +1 -1
  20. package/lib/esm/components/SelectorContainer/ApiSpecSelector.js +4 -3
  21. package/lib/esm/components/SelectorContainer/ApiSpecSelector.js.map +1 -1
  22. package/lib/esm/components/SideNav/SideNav.js +26 -16
  23. package/lib/esm/components/SideNav/SideNav.js.map +1 -1
  24. package/lib/esm/components/SideNav/SideNavMethods.js +8 -6
  25. package/lib/esm/components/SideNav/SideNavMethods.js.map +1 -1
  26. package/lib/esm/components/SideNav/SideNavTypes.js +8 -6
  27. package/lib/esm/components/SideNav/SideNavTypes.js.map +1 -1
  28. package/lib/esm/scenes/DiffScene/DiffScene.js +6 -6
  29. package/lib/esm/scenes/DiffScene/DiffScene.js.map +1 -1
  30. package/lib/esm/scenes/DiffScene/DocDiff/DiffItem.js +3 -4
  31. package/lib/esm/scenes/DiffScene/DocDiff/DiffItem.js.map +1 -1
  32. package/lib/esm/scenes/MethodScene/MethodScene.js +4 -3
  33. package/lib/esm/scenes/MethodScene/MethodScene.js.map +1 -1
  34. package/lib/esm/scenes/MethodTagScene/MethodTagScene.js +3 -2
  35. package/lib/esm/scenes/MethodTagScene/MethodTagScene.js.map +1 -1
  36. package/lib/esm/scenes/TypeScene/TypeScene.js +5 -1
  37. package/lib/esm/scenes/TypeScene/TypeScene.js.map +1 -1
  38. package/lib/esm/scenes/TypeTagScene/TypeTagScene.js +5 -5
  39. package/lib/esm/scenes/TypeTagScene/TypeTagScene.js.map +1 -1
  40. package/lib/esm/utils/hooks.js +26 -0
  41. package/lib/esm/utils/hooks.js.map +1 -0
  42. package/lib/esm/utils/index.js +1 -0
  43. package/lib/esm/utils/index.js.map +1 -1
  44. package/lib/esm/utils/path.js +2 -2
  45. package/lib/esm/utils/path.js.map +1 -1
  46. package/lib/scenes/DiffScene/DiffScene.js +4 -4
  47. package/lib/scenes/DiffScene/DiffScene.js.map +1 -1
  48. package/lib/scenes/DiffScene/DocDiff/DiffItem.js +2 -4
  49. package/lib/scenes/DiffScene/DocDiff/DiffItem.js.map +1 -1
  50. package/lib/scenes/MethodScene/MethodScene.js +3 -2
  51. package/lib/scenes/MethodScene/MethodScene.js.map +1 -1
  52. package/lib/scenes/MethodTagScene/MethodTagScene.js +2 -1
  53. package/lib/scenes/MethodTagScene/MethodTagScene.js.map +1 -1
  54. package/lib/scenes/TypeScene/TypeScene.js +6 -1
  55. package/lib/scenes/TypeScene/TypeScene.js.map +1 -1
  56. package/lib/scenes/TypeTagScene/TypeTagScene.js +3 -3
  57. package/lib/scenes/TypeTagScene/TypeTagScene.js.map +1 -1
  58. package/lib/utils/hooks.d.ts +3 -0
  59. package/lib/utils/hooks.js +36 -0
  60. package/lib/utils/hooks.js.map +1 -0
  61. package/lib/utils/index.d.ts +1 -0
  62. package/lib/utils/index.js +10 -1
  63. package/lib/utils/index.js.map +1 -1
  64. package/lib/utils/path.d.ts +2 -2
  65. package/lib/utils/path.js +2 -2
  66. package/lib/utils/path.js.map +1 -1
  67. package/package.json +9 -9
  68. package/src/ApiExplorer.tsx +7 -1
  69. package/src/components/DocMarkdown/DocMarkdown.tsx +4 -4
  70. package/src/components/SelectorContainer/ApiSpecSelector.spec.tsx +5 -2
  71. package/src/components/SelectorContainer/ApiSpecSelector.tsx +4 -3
  72. package/src/components/SideNav/SideNav.spec.tsx +39 -5
  73. package/src/components/SideNav/SideNav.tsx +22 -21
  74. package/src/components/SideNav/SideNavMethods.spec.tsx +9 -2
  75. package/src/components/SideNav/SideNavMethods.tsx +15 -8
  76. package/src/components/SideNav/SideNavTypes.spec.tsx +9 -2
  77. package/src/components/SideNav/SideNavTypes.tsx +15 -8
  78. package/src/scenes/DiffScene/DiffScene.tsx +6 -6
  79. package/src/scenes/DiffScene/DocDiff/DiffItem.spec.tsx +4 -3
  80. package/src/scenes/DiffScene/DocDiff/DiffItem.tsx +3 -4
  81. package/src/scenes/MethodScene/MethodScene.tsx +4 -3
  82. package/src/scenes/MethodTagScene/MethodTagScene.tsx +3 -2
  83. package/src/scenes/TypeScene/TypeScene.tsx +8 -2
  84. package/src/scenes/TypeTagScene/TypeTagScene.tsx +5 -5
  85. package/src/utils/hooks.spec.ts +78 -0
  86. package/src/utils/hooks.ts +53 -0
  87. package/src/utils/index.ts +1 -0
  88. package/src/utils/path.spec.ts +25 -2
  89. package/src/utils/path.ts +11 -4
@@ -91,7 +91,7 @@ export const ApiExplorer: FC<ApiExplorerProps> = ({
91
91
  const specs = useSelector(selectSpecs)
92
92
  const spec = useSelector(selectCurrentSpec)
93
93
  const { initLodesAction } = useLodeActions()
94
- const { initSettingsAction } = useSettingActions()
94
+ const { initSettingsAction, setSearchPatternAction } = useSettingActions()
95
95
  const { initSpecsAction, setCurrentSpecAction } = useSpecActions()
96
96
 
97
97
  const location = useLocation()
@@ -123,6 +123,12 @@ export const ApiExplorer: FC<ApiExplorerProps> = ({
123
123
  }
124
124
  }, [location.pathname, spec])
125
125
 
126
+ useEffect(() => {
127
+ const searchParams = new URLSearchParams(location.search)
128
+ const searchPattern = searchParams.get('s') || ''
129
+ setSearchPatternAction({ searchPattern: searchPattern! })
130
+ }, [location.search])
131
+
126
132
  useEffect(() => {
127
133
  if (headless) {
128
134
  window.addEventListener('message', hasNavigationToggle)
@@ -26,11 +26,11 @@
26
26
 
27
27
  import type { FC } from 'react'
28
28
  import React from 'react'
29
- import { useHistory } from 'react-router-dom'
30
29
  import { Markdown } from '@looker/code-editor'
31
30
  import { useSelector } from 'react-redux'
32
31
  import { getEnvAdaptor } from '@looker/extension-utils'
33
32
  import { selectSearchPattern } from '../../state'
33
+ import { useNavigation } from '../../utils'
34
34
  import { transformURL } from './utils'
35
35
 
36
36
  interface DocMarkdownProps {
@@ -40,13 +40,13 @@ interface DocMarkdownProps {
40
40
 
41
41
  export const DocMarkdown: FC<DocMarkdownProps> = ({ source, specKey }) => {
42
42
  const searchPattern = useSelector(selectSearchPattern)
43
- const history = useHistory()
43
+ const navigate = useNavigation()
44
44
 
45
45
  const linkClickHandler = (pathname: string, url: string) => {
46
46
  if (pathname.startsWith(`/${specKey}`)) {
47
- history.push(pathname)
47
+ navigate(pathname)
48
48
  } else if (url.startsWith(`/${specKey}`)) {
49
- history.push(url)
49
+ navigate(url)
50
50
  } else if (url.startsWith('https://')) {
51
51
  const adaptor = getEnvAdaptor()
52
52
  adaptor.openBrowserWindow(url)
@@ -41,7 +41,7 @@ jest.mock('react-router-dom', () => {
41
41
  useLocation: () => ({
42
42
  pathname: '/4.0/methods/Dashboard/dashboard',
43
43
  }),
44
- useHistory: jest.fn().mockReturnValue({ push: jest.fn() }),
44
+ useHistory: jest.fn().mockReturnValue({ push: jest.fn(), location }),
45
45
  }
46
46
  })
47
47
 
@@ -83,6 +83,9 @@ describe('ApiSpecSelector', () => {
83
83
  })
84
84
  const button = screen.getByText('3.1')
85
85
  userEvent.click(button)
86
- expect(push).toHaveBeenCalledWith('/3.1/methods/Dashboard/dashboard')
86
+ expect(push).toHaveBeenCalledWith({
87
+ pathname: '/3.1/methods/Dashboard/dashboard',
88
+ search: '',
89
+ })
87
90
  })
88
91
  })
@@ -27,9 +27,10 @@
27
27
  import type { FC } from 'react'
28
28
  import React from 'react'
29
29
  import { Select } from '@looker/components'
30
- import { useHistory, useLocation } from 'react-router-dom'
30
+ import { useLocation } from 'react-router-dom'
31
31
  import type { SpecItem } from '@looker/sdk-codegen'
32
32
  import { useSelector } from 'react-redux'
33
+ import { useNavigation } from '../../utils'
33
34
 
34
35
  import { selectSpecs } from '../../state'
35
36
 
@@ -38,8 +39,8 @@ interface ApiSpecSelectorProps {
38
39
  }
39
40
 
40
41
  export const ApiSpecSelector: FC<ApiSpecSelectorProps> = ({ spec }) => {
41
- const history = useHistory()
42
42
  const location = useLocation()
43
+ const navigate = useNavigation()
43
44
  const specs = useSelector(selectSpecs)
44
45
  const options = Object.entries(specs).map(([key, spec]) => ({
45
46
  value: key,
@@ -49,7 +50,7 @@ export const ApiSpecSelector: FC<ApiSpecSelectorProps> = ({ spec }) => {
49
50
 
50
51
  const handleChange = (specKey: string) => {
51
52
  const matchPath = location.pathname.replace(`/${spec.key}`, `/${specKey}`)
52
- history.push(matchPath)
53
+ navigate(matchPath)
53
54
  }
54
55
 
55
56
  return (
@@ -29,7 +29,10 @@ import userEvent from '@testing-library/user-event'
29
29
  import { screen, waitFor } from '@testing-library/react'
30
30
 
31
31
  import { getLoadedSpecs } from '../../test-data'
32
- import { renderWithRouterAndReduxProvider } from '../../test-utils'
32
+ import {
33
+ createTestStore,
34
+ renderWithRouterAndReduxProvider,
35
+ } from '../../test-utils'
33
36
  import { defaultSettingsState } from '../../state'
34
37
  import { SideNav } from './SideNav'
35
38
  import { countMethods, countTypes } from './searchUtils'
@@ -88,14 +91,45 @@ describe('SideNav', () => {
88
91
  })
89
92
  })
90
93
 
94
+ const mockHistoryPush = jest.fn()
95
+ jest.mock('react-router-dom', () => {
96
+ const ReactRouterDOM = jest.requireActual('react-router-dom')
97
+ return {
98
+ ...ReactRouterDOM,
99
+ useHistory: () => ({
100
+ push: mockHistoryPush,
101
+ location,
102
+ }),
103
+ }
104
+ })
105
+
91
106
  describe('Search', () => {
92
- test('it filters methods and types on input', async () => {
93
- renderWithRouterAndReduxProvider(<SideNav spec={spec} />)
107
+ test('inputting text in search box updates URL', async () => {
108
+ renderWithRouterAndReduxProvider(<SideNav spec={spec} />, ['/3.1/methods'])
94
109
  const searchPattern = 'embedsso'
95
110
  const input = screen.getByLabelText('Search')
96
- jest.spyOn(spec.api!, 'search')
97
- /** Pasting to avoid triggering search multiple times */
98
111
  await userEvent.paste(input, searchPattern)
112
+ await waitFor(() => {
113
+ expect(mockHistoryPush).toHaveBeenCalledWith({
114
+ pathname: '/3.1/methods',
115
+ search: `s=${searchPattern}`,
116
+ })
117
+ })
118
+ })
119
+
120
+ test('sets search default value from store on load', async () => {
121
+ const searchPattern = 'embedsso'
122
+ const store = createTestStore({
123
+ settings: { searchPattern: searchPattern },
124
+ })
125
+ jest.spyOn(spec.api!, 'search')
126
+ renderWithRouterAndReduxProvider(
127
+ <SideNav spec={spec} />,
128
+ ['/3.1/methods?s=embedsso'],
129
+ store
130
+ )
131
+ const input = screen.getByLabelText('Search')
132
+ expect(input).toHaveValue(searchPattern)
99
133
  await waitFor(() => {
100
134
  expect(spec.api!.search).toHaveBeenCalledWith(
101
135
  searchPattern,
@@ -26,7 +26,7 @@
26
26
 
27
27
  import type { FC } from 'react'
28
28
  import React, { useEffect, useState } from 'react'
29
- import { useHistory, useLocation } from 'react-router-dom'
29
+ import { useLocation } from 'react-router-dom'
30
30
  import {
31
31
  TabList,
32
32
  Tab,
@@ -44,10 +44,9 @@ import type {
44
44
  } from '@looker/sdk-codegen'
45
45
  import { criteriaToSet, tagTypes } from '@looker/sdk-codegen'
46
46
  import { useSelector } from 'react-redux'
47
-
48
- import { useWindowSize } from '../../utils'
47
+ import { useWindowSize, useNavigation } from '../../utils'
49
48
  import { HEADER_REM } from '../Header'
50
- import { selectSearchCriteria, useSettingActions } from '../../state'
49
+ import { selectSearchCriteria, selectSearchPattern } from '../../state'
51
50
  import { SideNavMethodTags } from './SideNavMethodTags'
52
51
  import { SideNavTypeTags } from './SideNavTypeTags'
53
52
  import { useDebounce, countMethods, countTypes } from './searchUtils'
@@ -68,8 +67,8 @@ interface SideNavProps {
68
67
  }
69
68
 
70
69
  export const SideNav: FC<SideNavProps> = ({ headless = false, spec }) => {
71
- const history = useHistory()
72
70
  const location = useLocation()
71
+ const navigate = useNavigation()
73
72
  const specKey = spec.key
74
73
  const tabNames = ['methods', 'types']
75
74
  const pathParts = location.pathname.split('/')
@@ -83,20 +82,19 @@ export const SideNav: FC<SideNavProps> = ({ headless = false, spec }) => {
83
82
  if (parts[1] === 'diff') {
84
83
  if (parts[3] !== tabNames[index]) {
85
84
  parts[3] = tabNames[index]
86
- history.push(parts.join('/'))
85
+ navigate(parts.join('/'))
87
86
  }
88
87
  } else {
89
88
  if (parts[2] !== tabNames[index]) {
90
89
  parts[2] = tabNames[index]
91
- history.push(parts.join('/'))
90
+ navigate(parts.join('/'))
92
91
  }
93
92
  }
94
93
  }
95
94
  const tabs = useTabs({ defaultIndex, onChange: onTabChange })
96
95
  const searchCriteria = useSelector(selectSearchCriteria)
97
- const { setSearchPatternAction } = useSettingActions()
98
-
99
- const [pattern, setSearchPattern] = useState('')
96
+ const searchPattern = useSelector(selectSearchPattern)
97
+ const [pattern, setSearchPattern] = useState(searchPattern)
100
98
  const debouncedPattern = useDebounce(pattern, 250)
101
99
  const [sideNavState, setSideNavState] = useState<SideNavState>(() => ({
102
100
  tags: spec?.api?.tags || {},
@@ -111,6 +109,17 @@ export const SideNav: FC<SideNavProps> = ({ headless = false, spec }) => {
111
109
  setSearchPattern(value)
112
110
  }
113
111
 
112
+ useEffect(() => {
113
+ const searchParams = new URLSearchParams(location.search)
114
+ if (debouncedPattern && debouncedPattern !== searchParams.get('s')) {
115
+ searchParams.set('s', debouncedPattern)
116
+ navigate(location.pathname, { search: searchParams.toString() })
117
+ } else if (!debouncedPattern && searchParams.get('s')) {
118
+ searchParams.delete('s')
119
+ navigate(location.pathname, { search: searchParams.toString() })
120
+ }
121
+ }, [location.search, debouncedPattern])
122
+
114
123
  useEffect(() => {
115
124
  let results
116
125
  let newTags
@@ -118,8 +127,8 @@ export const SideNav: FC<SideNavProps> = ({ headless = false, spec }) => {
118
127
  let newTypeTags
119
128
  const api = spec.api || ({} as ApiModel)
120
129
 
121
- if (debouncedPattern && api.search) {
122
- results = api.search(pattern, criteriaToSet(searchCriteria))
130
+ if (searchPattern && api.search) {
131
+ results = api.search(searchPattern, criteriaToSet(searchCriteria))
123
132
  newTags = results.tags
124
133
  newTypes = results.types
125
134
  newTypeTags = tagTypes(api, results.types)
@@ -136,15 +145,7 @@ export const SideNav: FC<SideNavProps> = ({ headless = false, spec }) => {
136
145
  methodCount: countMethods(newTags),
137
146
  searchResults: results,
138
147
  })
139
- setSearchPatternAction({ searchPattern: debouncedPattern })
140
- }, [
141
- debouncedPattern,
142
- specKey,
143
- spec,
144
- setSearchPatternAction,
145
- pattern,
146
- searchCriteria,
147
- ])
148
+ }, [searchPattern, specKey, spec, searchCriteria])
148
149
 
149
150
  useEffect(() => {
150
151
  const { selectedIndex, onSelectTab } = tabs
@@ -42,6 +42,7 @@ jest.mock('react-router-dom', () => {
42
42
  ...ReactRouterDOM,
43
43
  useHistory: () => ({
44
44
  push: mockHistoryPush,
45
+ location,
45
46
  }),
46
47
  }
47
48
  })
@@ -71,7 +72,10 @@ describe('SideNavMethods', () => {
71
72
  const firstMethod = Object.values(methods)[0].schema.summary
72
73
  expect(screen.queryByText(firstMethod)).not.toBeInTheDocument()
73
74
  userEvent.click(screen.getByText(tag))
74
- expect(mockHistoryPush).toHaveBeenCalledWith(`/${specKey}/methods/${tag}`)
75
+ expect(mockHistoryPush).toHaveBeenCalledWith({
76
+ pathname: `/${specKey}/methods/${tag}`,
77
+ search: '',
78
+ })
75
79
  expect(screen.getByRole('link', { name: firstMethod })).toBeInTheDocument()
76
80
  expect(screen.getAllByRole('link')).toHaveLength(
77
81
  Object.values(methods).length
@@ -93,7 +97,10 @@ describe('SideNavMethods', () => {
93
97
  Object.values(methods).length
94
98
  )
95
99
  userEvent.click(screen.getByText(tag))
96
- expect(mockHistoryPush).toHaveBeenCalledWith(`/${specKey}/methods`)
100
+ expect(mockHistoryPush).toHaveBeenCalledWith({
101
+ pathname: `/${specKey}/methods`,
102
+ search: '',
103
+ })
97
104
  expect(screen.queryByText(firstMethod)).not.toBeInTheDocument()
98
105
  expect(screen.queryByRole('link')).not.toBeInTheDocument()
99
106
  })
@@ -29,10 +29,9 @@ import styled from 'styled-components'
29
29
  import { Accordion2, Heading } from '@looker/components'
30
30
  import type { MethodList } from '@looker/sdk-codegen'
31
31
  import { useSelector } from 'react-redux'
32
- import { useHistory, useRouteMatch } from 'react-router-dom'
33
-
32
+ import { useLocation, useRouteMatch } from 'react-router-dom'
33
+ import { useNavigation, highlightHTML, buildMethodPath } from '../../utils'
34
34
  import { Link } from '../Link'
35
- import { buildMethodPath, highlightHTML } from '../../utils'
36
35
  import { selectSearchPattern } from '../../state'
37
36
 
38
37
  interface MethodsProps {
@@ -45,20 +44,21 @@ interface MethodsProps {
45
44
 
46
45
  export const SideNavMethods = styled(
47
46
  ({ className, methods, tag, specKey, defaultOpen = false }: MethodsProps) => {
47
+ const location = useLocation()
48
+ const navigate = useNavigation()
49
+ const searchParams = new URLSearchParams(location.search)
48
50
  const searchPattern = useSelector(selectSearchPattern)
49
51
  const match = useRouteMatch<{ methodTag: string }>(
50
52
  `/:specKey/methods/:methodTag/:methodName?`
51
53
  )
52
54
  const [isOpen, setIsOpen] = useState(defaultOpen)
53
- const history = useHistory()
54
-
55
55
  const handleOpen = () => {
56
56
  const _isOpen = !isOpen
57
57
  setIsOpen(_isOpen)
58
58
  if (_isOpen) {
59
- history.push(`/${specKey}/methods/${tag}`)
59
+ navigate(`/${specKey}/methods/${tag}`)
60
60
  } else {
61
- history.push(`/${specKey}/methods`)
61
+ navigate(`/${specKey}/methods`)
62
62
  }
63
63
  }
64
64
 
@@ -84,7 +84,14 @@ export const SideNavMethods = styled(
84
84
  <ul>
85
85
  {Object.values(methods).map((method) => (
86
86
  <li key={method.name}>
87
- <Link to={`${buildMethodPath(specKey, tag, method.name)}`}>
87
+ <Link
88
+ to={`${buildMethodPath(
89
+ specKey,
90
+ tag,
91
+ method.name,
92
+ searchParams.toString()
93
+ )}`}
94
+ >
88
95
  {highlightHTML(searchPattern, method.summary)}
89
96
  </Link>
90
97
  </li>
@@ -41,6 +41,7 @@ jest.mock('react-router-dom', () => {
41
41
  ...ReactRouterDOM,
42
42
  useHistory: () => ({
43
43
  push: mockHistoryPush,
44
+ location,
44
45
  }),
45
46
  }
46
47
  })
@@ -63,7 +64,10 @@ describe('SideNavTypes', () => {
63
64
  )
64
65
  expect(screen.queryByText(typeTags[0])).not.toBeInTheDocument()
65
66
  userEvent.click(screen.getByText(tag))
66
- expect(mockHistoryPush).toHaveBeenCalledWith(`/${specKey}/types/${tag}`)
67
+ expect(mockHistoryPush).toHaveBeenCalledWith({
68
+ pathname: `/${specKey}/types/${tag}`,
69
+ search: '',
70
+ })
67
71
  expect(screen.getByRole('link', { name: typeTags[0] })).toBeInTheDocument()
68
72
  })
69
73
 
@@ -78,7 +82,10 @@ describe('SideNavTypes', () => {
78
82
  )
79
83
  expect(screen.getByRole('link', { name: typeTags[0] })).toBeInTheDocument()
80
84
  userEvent.click(screen.getAllByText(tag)[0])
81
- expect(mockHistoryPush).toHaveBeenCalledWith(`/${specKey}/types`)
85
+ expect(mockHistoryPush).toHaveBeenCalledWith({
86
+ pathname: `/${specKey}/types`,
87
+ search: '',
88
+ })
82
89
  expect(
83
90
  screen.queryByRole('link', { name: typeTags[0] })
84
91
  ).not.toBeInTheDocument()
@@ -28,11 +28,10 @@ import React, { useEffect, useState } from 'react'
28
28
  import styled from 'styled-components'
29
29
  import { Accordion2, Heading } from '@looker/components'
30
30
  import type { TypeList } from '@looker/sdk-codegen'
31
- import { useHistory, useRouteMatch } from 'react-router-dom'
31
+ import { useLocation, useRouteMatch } from 'react-router-dom'
32
32
  import { useSelector } from 'react-redux'
33
-
34
33
  import { Link } from '../Link'
35
- import { buildTypePath, highlightHTML } from '../../utils'
34
+ import { highlightHTML, useNavigation, buildTypePath } from '../../utils'
36
35
  import { selectSearchPattern } from '../../state'
37
36
 
38
37
  interface TypesProps {
@@ -45,20 +44,21 @@ interface TypesProps {
45
44
 
46
45
  export const SideNavTypes = styled(
47
46
  ({ className, types, tag, specKey, defaultOpen = false }: TypesProps) => {
47
+ const location = useLocation()
48
+ const navigate = useNavigation()
49
+ const searchParams = new URLSearchParams(location.search)
48
50
  const searchPattern = useSelector(selectSearchPattern)
49
51
  const match = useRouteMatch<{ typeTag: string }>(
50
52
  `/:specKey/types/:typeTag/:typeName?`
51
53
  )
52
54
  const [isOpen, setIsOpen] = useState(defaultOpen)
53
- const history = useHistory()
54
-
55
55
  const handleOpen = () => {
56
56
  const _isOpen = !isOpen
57
57
  setIsOpen(_isOpen)
58
58
  if (_isOpen) {
59
- history.push(`/${specKey}/types/${tag}`)
59
+ navigate(`/${specKey}/types/${tag}`)
60
60
  } else {
61
- history.push(`/${specKey}/types`)
61
+ navigate(`/${specKey}/types`)
62
62
  }
63
63
  }
64
64
 
@@ -84,7 +84,14 @@ export const SideNavTypes = styled(
84
84
  <ul>
85
85
  {Object.values(types).map((type) => (
86
86
  <li key={type.name}>
87
- <Link to={`${buildTypePath(specKey, tag, type.name)}`}>
87
+ <Link
88
+ to={`${buildTypePath(
89
+ specKey,
90
+ tag,
91
+ type.name,
92
+ searchParams.toString()
93
+ )}`}
94
+ >
88
95
  {highlightHTML(searchPattern, type.name)}
89
96
  </Link>
90
97
  </li>
@@ -27,7 +27,7 @@
27
27
  import type { FC } from 'react'
28
28
  import React, { useState, useEffect } from 'react'
29
29
  import type { ApiModel, DiffRow, SpecList } from '@looker/sdk-codegen'
30
- import { useHistory, useRouteMatch } from 'react-router-dom'
30
+ import { useRouteMatch } from 'react-router-dom'
31
31
  import {
32
32
  Box,
33
33
  Flex,
@@ -42,7 +42,7 @@ import { useSelector } from 'react-redux'
42
42
 
43
43
  import { ApixSection } from '../../components'
44
44
  import { selectCurrentSpec } from '../../state'
45
- import { diffPath, getApixAdaptor } from '../../utils'
45
+ import { diffPath, getApixAdaptor, useNavigation } from '../../utils'
46
46
  import { diffSpecs, standardDiffToggles } from './diffUtils'
47
47
  import { DocDiff } from './DocDiff'
48
48
 
@@ -84,7 +84,7 @@ const validateParam = (specs: SpecList, specKey = '') => {
84
84
 
85
85
  export const DiffScene: FC<DiffSceneProps> = ({ specs, toggleNavigation }) => {
86
86
  const adaptor = getApixAdaptor()
87
- const history = useHistory()
87
+ const navigate = useNavigation()
88
88
  const spec = useSelector(selectCurrentSpec)
89
89
  const currentSpecKey = spec.key
90
90
  const match = useRouteMatch<{ l: string; r: string }>(`/${diffPath}/:l?/:r?`)
@@ -123,14 +123,14 @@ export const DiffScene: FC<DiffSceneProps> = ({ specs, toggleNavigation }) => {
123
123
  const [delta, setDelta] = useState<DiffRow[]>([])
124
124
 
125
125
  const handleLeftChange = (newLeft: string) => {
126
- history.push(`/${diffPath}/${newLeft}/${rightKey}`)
126
+ navigate(`/${diffPath}/${newLeft}/${rightKey}`)
127
127
  }
128
128
  const handleRightChange = (newRight: string) => {
129
- history.push(`/${diffPath}/${leftKey}/${newRight}`)
129
+ navigate(`/${diffPath}/${leftKey}/${newRight}`)
130
130
  }
131
131
 
132
132
  const handleSwitch = () => {
133
- history.push(`/${diffPath}/${rightKey}/${leftKey}`)
133
+ navigate(`/${diffPath}/${rightKey}/${leftKey}`)
134
134
  }
135
135
 
136
136
  useEffect(() => {
@@ -51,9 +51,10 @@ describe('DiffMethodLink', () => {
51
51
  const link = screen.getByRole('link')
52
52
  expect(link).toHaveTextContent(`${method.name} for ${specKey}`)
53
53
  fireEvent.click(link)
54
- expect(pushSpy).toHaveBeenCalledWith(
55
- `/${specKey}/methods/${method.schema.tags[0]}/${method.name}`
56
- )
54
+ expect(pushSpy).toHaveBeenCalledWith({
55
+ pathname: `/${specKey}/methods/${method.schema.tags[0]}/${method.name}`,
56
+ search: '',
57
+ })
57
58
  })
58
59
 
59
60
  test('it renders missing method and does not navigate on click', () => {
@@ -25,7 +25,6 @@
25
25
  */
26
26
  import type { FC } from 'react'
27
27
  import React, { useState, useEffect } from 'react'
28
- import { useHistory } from 'react-router'
29
28
  import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer'
30
29
  import styled from 'styled-components'
31
30
  import { Accordion2, Box, Card, Grid, Heading, Link } from '@looker/components'
@@ -33,7 +32,7 @@ import type { DiffRow } from '@looker/sdk-codegen/src'
33
32
  import type { ApiModel, IMethod } from '@looker/sdk-codegen'
34
33
  import { useSelector } from 'react-redux'
35
34
  import { selectSdkLanguage } from '../../../state'
36
- import { buildMethodPath } from '../../../utils'
35
+ import { buildMethodPath, useNavigation } from '../../../utils'
37
36
  import { DiffBanner } from './DiffBanner'
38
37
  import { differ } from './docDiffUtils'
39
38
 
@@ -60,7 +59,7 @@ export const DiffMethodLink: FC<DiffMethodLinkProps> = ({
60
59
  method,
61
60
  specKey,
62
61
  }) => {
63
- const history = useHistory()
62
+ const navigate = useNavigation()
64
63
 
65
64
  if (!method) return <Heading as="h4">{`Missing in ${specKey}`}</Heading>
66
65
 
@@ -71,7 +70,7 @@ export const DiffMethodLink: FC<DiffMethodLinkProps> = ({
71
70
  <DiffLink
72
71
  role="link"
73
72
  onClick={() => {
74
- history.push(path)
73
+ navigate(path)
75
74
  }}
76
75
  >{`${method.name} for ${specKey}`}</DiffLink>
77
76
  )
@@ -42,7 +42,7 @@ import { typeRefs } from '@looker/sdk-codegen'
42
42
  import { useSelector } from 'react-redux'
43
43
  import type { IEnvironmentAdaptor } from '@looker/extension-utils'
44
44
 
45
- import { getApixAdaptor } from '../../utils'
45
+ import { getApixAdaptor, useNavigation } from '../../utils'
46
46
  import {
47
47
  ApixSection,
48
48
  DocActivityType,
@@ -78,6 +78,7 @@ const showRunIt = async (adaptor: IEnvironmentAdaptor) => {
78
78
  export const MethodScene: FC<MethodSceneProps> = ({ api }) => {
79
79
  const adaptor = getApixAdaptor()
80
80
  const history = useHistory()
81
+ const navigate = useNavigation()
81
82
  const sdkLanguage = useSelector(selectSdkLanguage)
82
83
  const { specKey, methodTag, methodName } = useParams<MethodSceneParams>()
83
84
  const { value, toggle, setOn } = useToggle()
@@ -94,9 +95,9 @@ export const MethodScene: FC<MethodSceneProps> = ({ api }) => {
94
95
  // Invalid method
95
96
  if (api.tags[methodTag]) {
96
97
  // Found tag though
97
- history.push(`/${specKey}/methods/${methodTag}`)
98
+ navigate(`/${specKey}/methods/${methodTag}`)
98
99
  } else {
99
- history.push(`/${specKey}/methods`)
100
+ navigate(`/${specKey}/methods`)
100
101
  }
101
102
  }
102
103
  }, [api, history, methodName, methodTag, specKey])
@@ -29,7 +29,7 @@ import { useHistory, useParams } from 'react-router-dom'
29
29
  import { Grid, ButtonToggle, ButtonItem } from '@looker/components'
30
30
  import type { ApiModel } from '@looker/sdk-codegen'
31
31
  import { ApixSection, DocTitle, DocMethodSummary, Link } from '../../components'
32
- import { buildMethodPath } from '../../utils'
32
+ import { buildMethodPath, useNavigation } from '../../utils'
33
33
  import { getOperations } from './utils'
34
34
 
35
35
  interface MethodTagSceneProps {
@@ -44,6 +44,7 @@ interface MethodTagSceneParams {
44
44
  export const MethodTagScene: FC<MethodTagSceneProps> = ({ api }) => {
45
45
  const { specKey, methodTag } = useParams<MethodTagSceneParams>()
46
46
  const history = useHistory()
47
+ const navigate = useNavigation()
47
48
  const [value, setValue] = useState('ALL')
48
49
 
49
50
  useEffect(() => {
@@ -54,7 +55,7 @@ export const MethodTagScene: FC<MethodTagSceneProps> = ({ api }) => {
54
55
  const methods = api.tags[methodTag]
55
56
  useEffect(() => {
56
57
  if (!methods) {
57
- history.push(`/${specKey}/methods`)
58
+ navigate(`/${specKey}/methods`)
58
59
  }
59
60
  }, [history, methods])
60
61
  if (!methods) {
@@ -40,6 +40,7 @@ import {
40
40
  ExploreType,
41
41
  DocSchema,
42
42
  } from '../../components'
43
+ import { useNavigation } from '../../utils'
43
44
 
44
45
  interface DocTypeProps {
45
46
  api: ApiModel
@@ -47,19 +48,24 @@ interface DocTypeProps {
47
48
 
48
49
  interface DocTypeParams {
49
50
  specKey: string
51
+ typeTag: string
50
52
  typeName: string
51
53
  }
52
54
 
53
55
  export const TypeScene: FC<DocTypeProps> = ({ api }) => {
54
- const { specKey, typeName } = useParams<DocTypeParams>()
56
+ const { specKey, typeTag, typeName } = useParams<DocTypeParams>()
55
57
  const type = api.types[typeName]
56
58
  const history = useHistory()
59
+ const navigate = useNavigation()
57
60
  const typesUsed = typeRefs(api, type?.customTypes)
58
61
  const methodsUsedBy = methodRefs(api, type?.methodRefs)
59
62
  const typesUsedBy = typeRefs(api, type?.parentTypes)
60
63
  useEffect(() => {
61
64
  if (!type) {
62
- history.push(`/${specKey}/types`)
65
+ const route = api.typeTags[typeTag]
66
+ ? `/${specKey}/types/${typeTag}`
67
+ : `/${specKey}/types`
68
+ navigate(route)
63
69
  }
64
70
  }, [history, specKey, type])
65
71
 
@@ -27,9 +27,9 @@ import type { FC } from 'react'
27
27
  import React, { useEffect, useState } from 'react'
28
28
  import { Grid, ButtonToggle, ButtonItem } from '@looker/components'
29
29
  import type { ApiModel } from '@looker/sdk-codegen'
30
- import { useParams, useHistory } from 'react-router-dom'
30
+ import { useParams } from 'react-router-dom'
31
31
  import { ApixSection, DocTitle, DocTypeSummary, Link } from '../../components'
32
- import { buildTypePath } from '../../utils'
32
+ import { buildTypePath, useNavigation } from '../../utils'
33
33
  import { getMetaTypes } from './utils'
34
34
 
35
35
  interface TypeTagSceneProps {
@@ -43,7 +43,7 @@ interface TypeTagSceneParams {
43
43
 
44
44
  export const TypeTagScene: FC<TypeTagSceneProps> = ({ api }) => {
45
45
  const { specKey, typeTag } = useParams<TypeTagSceneParams>()
46
- const history = useHistory()
46
+ const navigate = useNavigation()
47
47
  const [value, setValue] = useState('ALL')
48
48
 
49
49
  useEffect(() => {
@@ -54,9 +54,9 @@ export const TypeTagScene: FC<TypeTagSceneProps> = ({ api }) => {
54
54
  const types = api.typeTags[typeTag]
55
55
  useEffect(() => {
56
56
  if (!types) {
57
- history.push(`/${specKey}/types`)
57
+ navigate(`/${specKey}/types`)
58
58
  }
59
- }, [history, types])
59
+ }, [types])
60
60
 
61
61
  if (!types) {
62
62
  return <></>