Skip to content

Commit

Permalink
docs-sidenav to TypeScript (#576)
Browse files Browse the repository at this point in the history
* feat: `docs-sidenav` to TypeScript

* chore: changeset

* chore: remove comments

* chore: changelog major change

* chore: fix input types; add return types

* chore(docs-page): add `platform-product-meta`
  • Loading branch information
thiskevinwang committed Apr 26, 2022
1 parent 1274422 commit 36f924a
Show file tree
Hide file tree
Showing 18 changed files with 222 additions and 126 deletions.
7 changes: 7 additions & 0 deletions .changeset/smart-owls-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@hashicorp/react-docs-sidenav': major
'@hashicorp/react-docs-page': major
---

- convert `docs-sidenav` to typescript
- update `docs-page` internal code and narrow prop type
24 changes: 15 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions packages/docs-page/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import HashiHead from '@hashicorp/react-head'
import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote'
import { SearchProvider } from '@hashicorp/react-search'
import { AlgoliaConfigObject } from '@hashicorp/react-search/types'
import type { Products } from '@hashicorp/platform-product-meta'

import VersionSelect from '@hashicorp/react-version-select'
import { getVersionFromPath } from '@hashicorp/react-version-select/util'
Expand All @@ -32,7 +33,7 @@ interface DocsPageInnerProps {
pageTitle: string
baseRoute: string
githubFileUrl: string
product: { name: string; slug: string }
product: { name: string; slug: Products }
/** @deprecated */
showEditPage: boolean
showVersionSelect: boolean
Expand Down Expand Up @@ -160,7 +161,7 @@ export const DocsPageInner: FunctionComponent<DocsPageInnerProps> = ({
}

export interface DocsPageProps {
product: { name: string; slug: string }
product: { name: string; slug: Products }
baseRoute: string
showEditPage?: boolean
showVersionSelect?: boolean
Expand Down
1 change: 1 addition & 0 deletions packages/docs-page/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"dependencies": {
"@hashicorp/platform-docs-mdx": "^0.1.4",
"@hashicorp/platform-markdown-utils": "^0.2.0",
"@hashicorp/platform-product-meta": "^0.1.0",
"@hashicorp/react-alert": "^6.0.2",
"@hashicorp/react-content": "^8.2.2",
"@hashicorp/react-docs-sidenav": "^8.4.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/docs-page/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { DataLoader } from './loaders/types'
export { getNodeFromPath } from './get-node-from-path'
export { getPathsFromNavData } from './get-paths-from-nav-data'
export { validateNavData } from './validate-nav-data'
export { validateFilePaths } from '@hashicorp/react-docs-sidenav/utils/validate-file-paths'
export { default as validateFilePaths } from '@hashicorp/react-docs-sidenav/utils/validate-file-paths'

interface BaseOpts {
fallback?: GetStaticPathsResult['fallback']
Expand Down
29 changes: 17 additions & 12 deletions packages/docs-page/server/validate-nav-data.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ jest.mock('@hashicorp/react-docs-sidenav/utils/validate-file-paths')
jest.mock('@hashicorp/react-docs-sidenav/utils/validate-route-structure')
jest.mock('@hashicorp/react-docs-sidenav/utils/validate-unlinked-content')

// Future: when '@hashicorp/react-docs-sidenav/utils' are converted to TS

// import { mocked } from 'ts-jest/utils'
// const mockedValidateFilePaths = mocked(validateFilePaths)
// const mockedValidateRouteStructure = mocked(validateRouteStructure)
// const mockedValidateUnlinkedContent = mocked(validateUnlinkedContent)
import { mocked } from 'jest-mock'
const mockedValidateFilePaths = mocked(validateFilePaths)
const mockedValidateRouteStructure = mocked(validateRouteStructure)
const mockedValidateUnlinkedContent = mocked(validateUnlinkedContent)

const CONTENT_DIR = 'content/commands'

Expand All @@ -32,15 +30,22 @@ describe('validateNavData', () => {
})

it('should validate nav data', async () => {
validateFilePaths.mockImplementation(async () => validateFilePathsResult)
validateRouteStructure.mockImplementation(async () => undefined)
validateUnlinkedContent.mockImplementation(async () => [])
mockedValidateFilePaths.mockImplementation(
async () => validateFilePathsResult
)
mockedValidateRouteStructure.mockImplementation(async () => undefined)
mockedValidateUnlinkedContent.mockImplementation(async () => [])

const res = await validateNavData(navData, CONTENT_DIR)

expect(validateFilePaths).toHaveBeenCalledWith(navData, 'content/commands')
expect(validateRouteStructure).toHaveBeenCalledWith(validateFilePathsResult)
expect(validateUnlinkedContent).toHaveBeenCalledWith(
expect(mockedValidateFilePaths).toHaveBeenCalledWith(
navData,
'content/commands'
)
expect(mockedValidateRouteStructure).toHaveBeenCalledWith(
validateFilePathsResult
)
expect(mockedValidateUnlinkedContent).toHaveBeenCalledWith(
navData,
'content/commands'
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { render, fireEvent, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import DocsSidenav from './'
import DocsSidenav from '.'
import props from './props'
import { getTestValues } from 'swingset/testing'

Expand All @@ -9,47 +9,50 @@ const defaultProps = getTestValues(props)
describe('<DocsSidenav />', () => {
it('renders a root element with a g-docs-sidenav className', () => {
const { container } = render(<DocsSidenav {...defaultProps} />)
expect(container.firstChild.className).toContain('g-docs-sidenav')
expect((container.firstChild as HTMLDivElement).className).toContain(
'g-docs-sidenav'
)
})

it('renders and displays nesting levels correctly', () => {
render(<DocsSidenav {...defaultProps} />)

// For this test, we step through the expected nesting levels based on
// the fixture data, ensuring that each level is nested properly and has
// the classes to reflect whether it's shown as active
const branchOne = screen.getByText('Vault Agent').closest('button')
const branchOne = screen.getByText('Vault Agent').closest('button')!
expect(branchOne.getAttribute('data-is-active')).toBe('true')
expect(branchOne.getAttribute('data-is-open')).toBe('true')

const branchTwo = screen
.getByText(
'Auto-Auth With A Very Long NavCategory Title That Should Wrap To Multiple Lines'
)
.closest('button')
.closest('button')!
expect(branchTwo.getAttribute('data-is-active')).toBe('true')
expect(branchTwo.getAttribute('data-is-open')).toBe('true')

const branchThree = screen.getByText('Methods').closest('button')
const branchThree = screen.getByText('Methods').closest('button')!
expect(branchThree.getAttribute('data-is-active')).toBe('true')
expect(branchThree.getAttribute('data-is-open')).toBe('true')

const activeLeaf = screen.getByText('AWS').closest('a')
const activeLeaf = screen.getByText('AWS').closest('a')!
expect(activeLeaf.getAttribute('data-is-active')).toBe('true')

// Let's also make sure that other pages are not also displaying as active
// First we check an similarly named page at a different level
const inactiveLeafOne = screen.getByText('AWS Agent').closest('a')
const inactiveLeafOne = screen.getByText('AWS Agent').closest('a')!
expect(inactiveLeafOne.getAttribute('data-is-active')).toBe('false')
// Next we check a page at the same level but with a different name
const inactiveLeafTwo = screen.getByText('GCP').closest('a')
const inactiveLeafTwo = screen.getByText('GCP').closest('a')!
expect(inactiveLeafTwo.getAttribute('data-is-active')).toBe('false')
// Finally we check the overview page at the same level
const inactiveLeafThree = screen
.getAllByText('Overview')
.map((node) => node.closest('a'))
.map((node) => node.closest('a')!)
.filter((linkElem) => {
return linkElem.getAttribute('href') === '/docs/agent/autoauth/methods'
})[0]
})[0]!
expect(inactiveLeafThree.getAttribute('data-is-active')).toBe('false')
})

Expand All @@ -60,10 +63,10 @@ describe('<DocsSidenav />', () => {
// Check the "overview" index node we've set as active using currentPath
const activeIndexLeaf = screen
.getAllByText('Overview')
.map((node) => node.closest('a'))
.map((node) => node.closest('a')!)
.filter((linkElem) => {
return linkElem.getAttribute('href') === expectedHref
})[0]
})[0]!
expect(activeIndexLeaf.getAttribute('data-is-active')).toBe('true')
})

Expand Down Expand Up @@ -94,7 +97,7 @@ describe('<DocsSidenav />', () => {
.getByText(
'Auto-Auth With A Very Long NavCategory Title That Should Wrap To Multiple Lines'
)
.closest('button')
.closest('button')!
expect(branchTwo.getAttribute('data-is-active')).toBe('true')
expect(branchTwo.getAttribute('data-is-open')).toBe('true')
// Click the item, then ensure it's closed, but still active
Expand All @@ -118,7 +121,7 @@ describe('<DocsSidenav />', () => {
// Get the menu button
const mobileMenuToggle = screen
.getByText('Documentation Menu')
.closest('button')
.closest('button')!
// Click the menu button, and check the sidebar opens
fireEvent.click(mobileMenuToggle)
expect(sidebarNavList.getAttribute('data-is-mobile-open')).toBe('true')
Expand All @@ -139,7 +142,8 @@ describe('<DocsSidenav />', () => {
// Get the search button
const searchToggle = screen
.getByLabelText('Show Search Bar')
.closest('button')
.closest('button')!

// Click the menu button, and check the sidebar opens
fireEvent.click(searchToggle)

Expand Down

1 comment on commit 36f924a

@vercel
Copy link

@vercel vercel bot commented on 36f924a Apr 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.