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

Bnr/copy wrapper #1122

Draft
wants to merge 33 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0b4e578
WIP search sharing in URL
patnir41 Jun 22, 2022
95436a4
URL search param will update search box in sidenav
patnir41 Jun 23, 2022
dcb882a
Avoid string | null errors
patnir41 Jun 24, 2022
850b64b
WIP search query back button and slow input box
patnir41 Jun 24, 2022
40caab1
Functional query routing with accurate browser history, WIP testing
patnir41 Jun 29, 2022
754c84f
Removed comment
patnir41 Jun 29, 2022
de28036
Added navigation utility file, helper function for pushing routing wi…
patnir41 Jun 30, 2022
d26203e
Functional with spec and search and method/types chosen
patnir41 Jul 1, 2022
1a013f1
WIP: mocking ApiExplorer.tsx useEffect on location search
patnir41 Jul 12, 2022
02afef8
Quick addition
patnir41 Jul 12, 2022
1c41c03
WIP: getting testing to work.
patnir41 Jul 13, 2022
a8a0451
WIP comments
patnir41 Jul 13, 2022
671d3dc
WIP: working SideNav tests
patnir41 Jul 13, 2022
281eb27
Tests working, need to now add final few
patnir41 Jul 13, 2022
1467970
New properly working navigate function with unit tests, working exist…
patnir41 Jul 13, 2022
1f5ac10
Refactoring some comments out, fixed to pass tests, removed APIExplor…
patnir41 Jul 14, 2022
2a664cd
Minor refactoring to add readability/cleanliness
patnir41 Jul 14, 2022
6cb274a
Per feedback from John and Joseph, WIP: navigation hook
patnir41 Jul 15, 2022
516c106
Created custom navigation hook, replaced history pushes where necessary
patnir41 Jul 15, 2022
0ed839a
Added changes per Bryn's comments, new buildNavigationPath
patnir41 Jul 16, 2022
addc668
updated edit for buildNavigationPath
patnir41 Jul 16, 2022
aaba996
Merge branch 'main' into search-query-route-update
jkaster Jul 18, 2022
bfdaf8f
Merge branch 'main' into search-query-route-update
jkaster Jul 18, 2022
1f2857e
Changes per Joseph's feedback
patnir41 Jul 19, 2022
7c3124e
Added location.search as dependency in SideNav useEffect
patnir41 Jul 19, 2022
e69bb82
Copy button functional within inner search box; need to migrate to se…
patnir41 Jul 16, 2022
0f5211f
Proper CopyLinkButton file set up
patnir41 Jul 18, 2022
7276e0a
URL icon updated with recent PR branch functional, less style component
patnir41 Jul 19, 2022
25d2d6c
Unit test for CopyLinkButton
patnir41 Jul 19, 2022
e4c7919
Unit tests + functionality fixed, using Span
patnir41 Jul 19, 2022
a4017d3
Merge branch 'main' into patnir41/url-sharing-icon
patnir41 Jul 19, 2022
bd4ced9
Added unit test to SideNav, refactored code
patnir41 Jul 20, 2022
e806099
copy wrapper
bryans99 Jul 21, 2022
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*

MIT License

Copyright (c) 2021 Looker Data Sciences, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

import { renderWithTheme } from '@looker/components-test-utils'
import { screen, waitFor } from '@testing-library/react'
import React from 'react'
import userEvent from '@testing-library/user-event'
import { CopyLinkButton } from '../CopyLinkButton'

describe('CopyLinkButton', () => {
test('it renders', () => {
renderWithTheme(<CopyLinkButton top={'1px'} right={'1px'} visible={true} />)
expect(screen.getByText('Copy link to this page view')).toBeInTheDocument()
})
const mockClipboardCopy = jest
.fn()
.mockImplementation(() => Promise.resolve())
Object.assign(navigator, {
clipboard: {
writeText: mockClipboardCopy,
},
})
test('it copies to clipboard', async () => {
jest.spyOn(navigator.clipboard, 'writeText')
renderWithTheme(<CopyLinkButton top={'1px'} right={'1px'} visible={true} />)
await waitFor(() => {
userEvent.click(screen.getByRole('button'))
expect(mockClipboardCopy).toHaveBeenCalledWith(location.href)
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*

MIT License

Copyright (c) 2021 Looker Data Sciences, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/
import React, { useState } from 'react'
import { IconButton } from '@looker/components'
import { Link } from '@styled-icons/material-outlined/Link'
import styled from 'styled-components'

interface DocumentInterfaceProps {
top: string
right: string
visible: boolean
}
export const CopyLinkButton = ({
top,
right,
visible,
}: DocumentInterfaceProps) => {
const [title, CopyLinkTooltip] = useState('Copy link to this page view')
return (
<CopyLink visible={visible} top={top} right={right}>
<IconButton
onClick={async () => {
CopyLinkTooltip('Copied to clipboard')
await navigator.clipboard.writeText(location.href)
}}
onMouseEnter={() => CopyLinkTooltip('Copy link to this section')}
size="small"
icon={<Link />}
label={title}
tooltipPlacement="bottom"
/>
</CopyLink>
)
}

const CopyLink = styled('span')<{
top: string
right: string
visible: boolean
}>`
position: absolute;
top: ${({ top }) => top};
right: ${({ right }) => right};
display: ${({ visible }) => (visible ? 'block' : 'none')};
`
26 changes: 26 additions & 0 deletions packages/api-explorer/src/components/CopyLinkButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*

MIT License

Copyright (c) 2021 Looker Data Sciences, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/
export { CopyLinkButton } from './CopyLinkButton'
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*

MIT License

Copyright (c) 2021 Looker Data Sciences, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/
import React, { ReactNode, ReactNodeArray, useState, useCallback } from 'react'
import { IconButton, Space } from '@looker/components'
import { Link } from '@styled-icons/material-outlined/Link'

interface CopyLinkWrapperProps {
visible?: boolean
children: ReactNode | ReactNodeArray
}

const COPY_TO_CLIPBOARD = 'Copied to clipboard'
const COPY_TO_SECTION = 'Copy link to this section'

export const CopyLinkWrapper = ({
visible = true,
children,
}: CopyLinkWrapperProps) => {
const [showCopyLinkButton, setShowCopyLinkButton] = useState(false)
const [tooltipContent, setTooltipContent] = useState(COPY_TO_SECTION)

const onCopyLink = useCallback(async () => {
setTooltipContent(COPY_TO_CLIPBOARD)
// TODO - this wont work in the extension - there are other mechanisms
await navigator.clipboard.writeText(location.href)
}, [setTooltipContent])

const onCopyButtonMouseLeave = useCallback(async () => {
setTooltipContent(COPY_TO_SECTION)
}, [setTooltipContent])

return (
<>
<Space
onMouseEnter={() => setShowCopyLinkButton(true)}
onMouseLeave={() => setShowCopyLinkButton(false)}
width="100%"
>
{children}
{visible && showCopyLinkButton && (
<IconButton
onClick={onCopyLink}
onMouseLeave={onCopyButtonMouseLeave}
size="small"
icon={<Link />}
label={tooltipContent}
tooltipPlacement="bottom"
/>
)}
</Space>
</>
)
}
26 changes: 26 additions & 0 deletions packages/api-explorer/src/components/CopyLinkWrapper/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*

MIT License

Copyright (c) 2021 Looker Data Sciences, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/
export { CopyLinkWrapper } from './CopyLinkWrapper'
12 changes: 12 additions & 0 deletions packages/api-explorer/src/components/SideNav/SideNav.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ describe('Search', () => {
})
})

test('renders and removes copy link button based on mouse hover', async () => {
renderWithRouterAndReduxProvider(<SideNav spec={spec} />, ['/3.1/methods'])
expect(screen.getByText('Copy link to this page view')).not.toBeVisible()
const searchPattern = 'embedsso'
const input = screen.getByLabelText('Search')
await userEvent.paste(input, searchPattern)
userEvent.hover(input)
expect(screen.getByText('Copy link to this page view')).toBeVisible()
userEvent.unhover(input)
expect(screen.getByText('Copy link to this page view')).not.toBeVisible()
})

test('sets search default value from store on load', async () => {
const searchPattern = 'embedsso'
const store = createTestStore({
Expand Down
27 changes: 19 additions & 8 deletions packages/api-explorer/src/components/SideNav/SideNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
TabPanels,
useTabs,
InputSearch,
Box2,
} from '@looker/components'
import type {
SpecItem,
Expand All @@ -51,6 +52,7 @@ import { SideNavMethodTags } from './SideNavMethodTags'
import { SideNavTypeTags } from './SideNavTypeTags'
import { useDebounce, countMethods, countTypes } from './searchUtils'
import { SearchMessage } from './SearchMessage'
import { CopyLinkWrapper } from '../CopyLinkWrapper'

interface SideNavState {
tags: TagList
Expand Down Expand Up @@ -95,6 +97,7 @@ export const SideNav: FC<SideNavProps> = ({ headless = false, spec }) => {
const searchCriteria = useSelector(selectSearchCriteria)
const searchPattern = useSelector(selectSearchPattern)
const [pattern, setSearchPattern] = useState(searchPattern)
const [showCopyLinkButton, setShowCopyLinkButton] = useState(false)
const debouncedPattern = useDebounce(pattern, 250)
const [sideNavState, setSideNavState] = useState<SideNavState>(() => ({
tags: spec?.api?.tags || {},
Expand All @@ -107,6 +110,7 @@ export const SideNav: FC<SideNavProps> = ({ headless = false, spec }) => {

const handleInputChange = (value: string) => {
setSearchPattern(value)
setShowCopyLinkButton(!!value)
}

useEffect(() => {
Expand Down Expand Up @@ -160,18 +164,25 @@ export const SideNav: FC<SideNavProps> = ({ headless = false, spec }) => {

return (
<nav>
<InputSearch
<Box2
pl="large"
pr="large"
pb="large"
pt={headless ? 'u3' : 'large'}
aria-label="Search"
onChange={handleInputChange}
placeholder="Search"
value={pattern}
isClearable
/>
<SearchMessage search={searchResults} />
position={'relative'}
width={'100%'}
>
<CopyLinkWrapper visible={showCopyLinkButton}>
<InputSearch
aria-label="Search"
onChange={handleInputChange}
placeholder="Search"
value={pattern}
isClearable
/>
</CopyLinkWrapper>
<SearchMessage search={searchResults} />
</Box2>
<TabList {...tabs} distribute>
<Tab>Methods ({methodCount})</Tab>
<Tab>Types ({typeCount})</Tab>
Expand Down
1 change: 1 addition & 0 deletions packages/api-explorer/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

*/
export { ApixSection, Loader } from './common'
export { CopyLinkButton } from './CopyLinkButton'
export { DocActivityType } from './DocActivityType'
export { DocCode } from './DocCode'
export { DocMethodSummary, DocSummaryStatus } from './DocMethodSummary'
Expand Down