From ff2f9e4bedfcdcace74b598eb63223df7216b428 Mon Sep 17 00:00:00 2001 From: Hanqing Huang Date: Wed, 11 Sep 2024 16:27:31 -0700 Subject: [PATCH 1/9] navigator gets data from IndexStore navigator gets data from IndexStore --- src/components/DocumentationLayout.vue | 98 +++++++++++++++----------- src/components/Navigator.vue | 16 ++--- src/views/DocumentationTopic.vue | 2 +- 3 files changed, 60 insertions(+), 56 deletions(-) diff --git a/src/components/DocumentationLayout.vue b/src/components/DocumentationLayout.vue index 65d4da948..d6f023286 100644 --- a/src/components/DocumentationLayout.vue +++ b/src/components/DocumentationLayout.vue @@ -30,46 +30,44 @@ > @@ -89,7 +87,6 @@ import { storage } from 'docc-render/utils/storage'; import { getSetting } from 'docc-render/utils/theme-settings'; import IndexStore from 'docc-render/stores/IndexStore'; -import NavigatorDataProvider from 'theme/components/Navigator/NavigatorDataProvider.vue'; import DocumentationNav from 'theme/components/DocumentationTopic/DocumentationNav.vue'; const NAVIGATOR_HIDDEN_ON_LARGE_KEY = 'navigator-hidden-large'; @@ -100,7 +97,6 @@ export default { components: { Navigator, AdjustableSidebarWidth, - NavigatorDataProvider, Nav: DocumentationNav, QuickNavigationButton, QuickNavigationModal, @@ -159,9 +155,25 @@ export default { enableQuickNavigation: ({ isTargetIDE }) => ( !isTargetIDE && getSetting(['features', 'docs', 'quickNavigation', 'enable'], true) ), - quickNavNodes({ indexState: { flatChildren = {} }, interfaceLanguage }) { + indexNodes({ indexState: { flatChildren = {} }, interfaceLanguage }) { return flatChildren[interfaceLanguage] ?? (flatChildren[Language.swift.key.url] || []); }, + navigatorProps: ({ + indexNodes, + indexState: { + references, + apiChanges, + errorFetching, + technologyProps, + }, + technology, + }) => ({ + flatChildren: indexNodes, + navigatorReferences: references, + apiChanges, + errorFetching, + technologyProps: technologyProps || technology, + }), sidebarProps: ({ sidenavVisibleOnMobile, enableNavigator, sidenavHiddenOnLarge, navigatorFixedWidth, }) => ( diff --git a/src/components/Navigator.vue b/src/components/Navigator.vue index bbc069faf..efc4e2739 100644 --- a/src/components/Navigator.vue +++ b/src/components/Navigator.vue @@ -91,14 +91,10 @@ export default { type: Array, required: true, }, - technology: { + technologyProps: { type: Object, required: false, }, - isFetching: { - type: Boolean, - default: false, - }, references: { type: Object, default: () => {}, @@ -125,6 +121,9 @@ export default { }, }, computed: { + isFetching({ flatChildren, errorFetching }) { + return !flatChildren.length && !errorFetching; + }, // gets the paths for each parent in the breadcrumbs parentTopicReferences({ references, parentTopicIdentifiers }) { return parentTopicIdentifiers @@ -153,13 +152,6 @@ export default { * The root item is always a module */ type: () => TopicTypes.module, - technologyProps: ({ technology }) => ( - !technology ? null : { - technology: technology.title, - technologyPath: technology.path || technology.url, - isTechnologyBeta: technology.beta, - } - ), }, }; diff --git a/src/views/DocumentationTopic.vue b/src/views/DocumentationTopic.vue index 47f16f48a..0ecb5be6f 100644 --- a/src/views/DocumentationTopic.vue +++ b/src/views/DocumentationTopic.vue @@ -52,7 +52,7 @@ import { shouldFetchDataForRouteUpdate, } from 'docc-render/utils/data'; import DocumentationTopic from 'theme/components/DocumentationTopic.vue'; -import DocumentationLayout from 'docc-render/components/DocumentationLayout.vue'; +import DocumentationLayout from 'theme/components/DocumentationLayout.vue'; import DocumentationTopicStore from 'docc-render/stores/DocumentationTopicStore'; import indexProvider from 'theme/mixins/indexProvider'; import Language from 'docc-render/constants/Language'; From 23b78b3d62ba175ea49db0c56ca2528d4139fe79 Mon Sep 17 00:00:00 2001 From: Hanqing Huang Date: Mon, 16 Sep 2024 11:34:05 -0700 Subject: [PATCH 2/9] Remove `NavigatorDataProvider` and its tests Remove `NavigatorDataProvider` and its tests --- .../Navigator/NavigatorDataProvider.vue | 125 ---- .../Navigator/NavigatorDataProvider.spec.js | 557 ----------------- .../NavigatorDataProvider.spec.js.snap | 559 ------------------ 3 files changed, 1241 deletions(-) delete mode 100644 src/components/Navigator/NavigatorDataProvider.vue delete mode 100644 tests/unit/components/Navigator/NavigatorDataProvider.spec.js delete mode 100644 tests/unit/components/Navigator/__snapshots__/NavigatorDataProvider.spec.js.snap diff --git a/src/components/Navigator/NavigatorDataProvider.vue b/src/components/Navigator/NavigatorDataProvider.vue deleted file mode 100644 index 26fc9c76a..000000000 --- a/src/components/Navigator/NavigatorDataProvider.vue +++ /dev/null @@ -1,125 +0,0 @@ - - - diff --git a/tests/unit/components/Navigator/NavigatorDataProvider.spec.js b/tests/unit/components/Navigator/NavigatorDataProvider.spec.js deleted file mode 100644 index f17394249..000000000 --- a/tests/unit/components/Navigator/NavigatorDataProvider.spec.js +++ /dev/null @@ -1,557 +0,0 @@ -/** - * This source file is part of the Swift.org open source project - * - * Copyright (c) 2022 Apple Inc. and the Swift project authors - * Licensed under Apache License v2.0 with Runtime Library Exception - * - * See https://swift.org/LICENSE.txt for license information - * See https://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ - -import NavigatorDataProvider from '@/components/Navigator/NavigatorDataProvider.vue'; -import { shallowMount } from '@vue/test-utils'; -import Language from 'docc-render/constants/Language'; -import { TopicTypes } from '@/constants/TopicTypes'; -import { fetchIndexPathsData } from '@/utils/data'; -import { INDEX_ROOT_KEY } from '@/constants/sidebar'; -import { flushPromises } from '../../../../test-utils'; - -jest.mock('docc-render/utils/data'); - -const technologyUrl = '/documentation/foo'; - -const extendedTechnologies = { - path: '/documentation/foo', - children: [ - { - id: 'Group Marker', - title: 'Group Marker', - type: TopicTypes.groupMarker, - }, - { - id: 'Child0', - title: 'Child0', - path: '/documentation/foo/child0', - type: 'article', - children: [ - { - id: 'Group Marker, Child 0', - title: 'Group Marker, Child 0', - type: TopicTypes.groupMarker, - }, - { - id: 'Child0_GrandChild0', - title: 'Child0_GrandChild0', - path: '/documentation/foo/child0/grandchild0', - type: 'tutorial', - }, - { - id: 'Child0_GrandChild1', - title: 'Child0_GrandChild1', - path: '/documentation/foo/child0/grandchild1', - type: 'tutorial', - children: [{ - id: 'Child0_GrandChild0_GreatGrandChild0', - title: 'Child0_GrandChild0_GreatGrandChild0', - path: '/documentation/foo/child0/grandchild0/greatgrandchild0', - type: 'tutorial', - }], - }, - { - id: 'Child0_GrandChild2', - title: 'Child0_GrandChild2', - path: '/documentation/foo/child0/grandchild2', - type: 'tutorial', - }, - ], - }, - { - id: 'Child1', - title: 'Child1', - path: '/documentation/foo/child1/', - type: 'tutorial', - children: [{ - id: 'Child1_GrandChild0', - title: 'Child1_GrandChild0', - path: '/documentation/foo/child1/grandchild0', - type: 'method', - }], - }, - ], -}; - -const flatChildren = [ - { - uid: -196255993, - parent: '', - index: 0, - siblingsCount: 3, - depth: 0, - childUIDs: [], - }, - { - uid: -196255992, - parent: '', - index: 1, - siblingsCount: 3, - depth: 0, - childUIDs: [], - }, - { - uid: -196255991, - parent: '', - index: 2, - siblingsCount: 3, - depth: 0, - childUIDs: [], - }, -]; - -const swiftIndexOne = { - id: 'foo', - path: technologyUrl, - children: [1, 2, 3], -}; -const objectiveCIndexOne = { - id: 'foo-objc', - path: technologyUrl, - children: [1], -}; - -const references = { - foo: { bar: 'bar' }, -}; - -const includedArchiveIdentifiers = [ - 'foo', - 'bar', -]; - -const response = { - includedArchiveIdentifiers, - interfaceLanguages: { - [Language.swift.key.url]: [ - swiftIndexOne, - ], - [Language.objectiveC.key.url]: [ - objectiveCIndexOne, - ], - }, - references, -}; - -fetchIndexPathsData.mockResolvedValue(response); - -const defaultProps = { - technologyUrl, -}; - -let props = {}; - -const createWrapper = ({ propsData, ...others } = {}) => shallowMount(NavigatorDataProvider, { - propsData: { - ...defaultProps, - ...propsData, - }, - scopedSlots: { - default: (p) => { - props = p; - return 'Text'; - }, - }, - mocks: { - $route: { - params: { - locale: 'en-US', - }, - }, - }, - ...others, -}); - -describe('NavigatorDataProvider', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('fetches data when mounting NavigatorDataProvider', async () => { - expect(fetchIndexPathsData).toHaveBeenCalledTimes(0); - createWrapper(); - expect(fetchIndexPathsData).toHaveBeenCalledTimes(1); - expect(props).toHaveProperty('isFetching', true); - await flushPromises(); - expect(props).toEqual({ - apiChanges: null, - isFetchingAPIChanges: false, - isFetching: false, - technology: swiftIndexOne, - errorFetching: false, - flatChildren, - references, - }); - }); - - it('fetches data, even if being passed a none-root technology url', async () => { - expect(fetchIndexPathsData).toHaveBeenCalledTimes(0); - createWrapper({ - propsData: { - technologyUrl: `${technologyUrl}/bar/baz`, - }, - }); - expect(fetchIndexPathsData).toHaveBeenCalledTimes(1); - expect(props).toHaveProperty('isFetching', true); - await flushPromises(); - expect(props).toEqual({ - apiChanges: null, - isFetchingAPIChanges: false, - isFetching: false, - technology: swiftIndexOne, - errorFetching: false, - flatChildren, - references, - }); - }); - - it('sets errorFetching to true, when request errored', async () => { - expect(fetchIndexPathsData).toHaveBeenCalledTimes(0); - fetchIndexPathsData.mockRejectedValueOnce('Error'); - createWrapper(); - expect(fetchIndexPathsData).toHaveBeenCalledTimes(1); - expect(props).toHaveProperty('isFetching', true); - await flushPromises(); - expect(props).toEqual({ - apiChanges: null, - isFetchingAPIChanges: false, - isFetching: false, - technology: undefined, - errorFetching: true, - flatChildren: [], - references: {}, - }); - }); - - it('returns objc data', async () => { - expect(fetchIndexPathsData).toHaveBeenCalledTimes(0); - createWrapper({ - propsData: { - interfaceLanguage: Language.objectiveC.key.url, - }, - }); - expect(fetchIndexPathsData).toHaveBeenCalledTimes(1); - expect(props).toHaveProperty('isFetching', true); - await flushPromises(); - expect(props).toEqual({ - apiChanges: null, - isFetchingAPIChanges: false, - errorFetching: false, - isFetching: false, - technology: objectiveCIndexOne, - flatChildren: [{ - uid: -196255993, - parent: '', - index: 0, - siblingsCount: 1, - depth: 0, - childUIDs: [], - }], - references, - }); - }); - - it('falls back to swift items, if no objc items', async () => { - expect(fetchIndexPathsData).toHaveBeenCalledTimes(0); - fetchIndexPathsData.mockResolvedValueOnce({ - interfaceLanguages: { - [Language.swift.key.url]: response.interfaceLanguages[Language.swift.key.url], - }, - references, - }); - createWrapper({ - propsData: { - interfaceLanguage: Language.objectiveC.key.url, - }, - }); - expect(fetchIndexPathsData).toHaveBeenCalledTimes(1); - expect(props).toHaveProperty('isFetching', true); - await flushPromises(); - expect(props).toEqual({ - apiChanges: null, - isFetchingAPIChanges: false, - errorFetching: false, - isFetching: false, - technology: swiftIndexOne, - flatChildren, - references, - }); - }); - - it('returns the first technology, if none matches', async () => { - createWrapper({ - propsData: { - technologyUrl: '/documentation/bar', - }, - }); - await flushPromises(); - expect(props).toEqual({ - apiChanges: null, - isFetchingAPIChanges: false, - errorFetching: false, - isFetching: false, - technology: swiftIndexOne, - flatChildren, - references, - }); - }); - - it('returns undefined technology when index response is empty', async () => { - const emptyResponse = { interfaceLanguages: {} }; - fetchIndexPathsData.mockResolvedValue(emptyResponse); - createWrapper(); - await flushPromises(); - expect(props).toEqual({ - apiChanges: null, - isFetchingAPIChanges: false, - isFetching: false, - technology: undefined, - errorFetching: false, - flatChildren: [], - references: undefined, - }); - }); - - it('counts the amount of deprecated items a groupMarker has', async () => { - const technologyClone = JSON.parse(JSON.stringify(extendedTechnologies)); - technologyClone.children[1].deprecated = true; - technologyClone.children[2].deprecated = true; - technologyClone.children[1].children[1].deprecated = true; - fetchIndexPathsData.mockResolvedValue({ - interfaceLanguages: { - [Language.swift.key.url]: [ - technologyClone, - ], - }, - }); - createWrapper(); - await flushPromises(); - expect(props.flatChildren[0]).toHaveProperty('deprecatedChildrenCount', 2); - expect(props.flatChildren).toMatchSnapshot(); - }); - - it('removes the `beta` flag from children, if the parent is a `beta`', async () => { - const technologyClone = JSON.parse(JSON.stringify(extendedTechnologies)); - technologyClone.beta = true; - technologyClone.children[1].beta = true; - technologyClone.children[1].children[0].beta = true; - fetchIndexPathsData.mockResolvedValue({ - interfaceLanguages: { - [Language.swift.key.url]: [ - technologyClone, - ], - }, - }); - createWrapper(); - await flushPromises(); - expect(props).toMatchSnapshot(); - }); - - it('removes the `beta` flag from children, if the parent is a `beta`', async () => { - const technologyClone = JSON.parse(JSON.stringify(extendedTechnologies)); - technologyClone.children[1].beta = true; - technologyClone.children[1].children[1].beta = true; - // case where the direct parent is NOT `Beta`, but an ancestor is - technologyClone.children[1].children[2].children[0].beta = true; - // set an end node as beta - technologyClone.children[2].children[0].beta = true; - fetchIndexPathsData.mockResolvedValue({ - interfaceLanguages: { - [Language.swift.key.url]: [ - technologyClone, - ], - }, - }); - createWrapper(); - await flushPromises(); - expect(props).toMatchSnapshot(); - }); - - it('flattens deeply nested children and provides them to the NavigatorCard', async () => { - fetchIndexPathsData.mockResolvedValue({ - interfaceLanguages: { - [Language.swift.key.url]: [{ - path: '/documentation/foo', - children: [ - { - title: 'Group Marker', - type: TopicTypes.groupMarker, - }, - { - title: 'Child0', - path: '/foo/child0', - type: 'article', - children: [ - { - title: 'Group Marker, Child 0', - type: TopicTypes.groupMarker, - }, - { - title: 'Child0_GrandChild0', - path: '/foo/child0/grandchild0', - type: 'tutorial', - }, - { - title: 'Child0_GrandChild1', - path: '/foo/child0/grandchild1', - type: 'tutorial', - children: [{ - title: 'Child0_GrandChild0_GreatGrandChild0', - path: '/foo/child0/grandchild0/greatgrandchild0', - type: 'tutorial', - }], - }, - { - title: 'Child0_GrandChild2', - path: '/foo/child0/grandchild2', - type: 'tutorial', - }, - ], - }, - { - title: 'Child1', - path: '/foo/child1/', - type: 'tutorial', - children: [{ - title: 'Child1_GrandChild0', - path: '/foo/child1/grandchild0', - type: 'method', - }], - }, - ], - }], - }, - }); - createWrapper(); - await flushPromises(); - expect(props.flatChildren).toEqual([ - { - childUIDs: [ - 551503844, - -97593391, - ], - deprecatedChildrenCount: 0, - depth: 0, - index: 0, - parent: INDEX_ROOT_KEY, - siblingsCount: 3, - title: 'Group Marker', - type: 'groupMarker', - uid: -196255993, - }, - { - childUIDs: [ - -361407047, - 1438225895, - 1439149417, - 1440072939, - ], - depth: 0, - groupMarkerUID: -196255993, - index: 1, - parent: INDEX_ROOT_KEY, - path: '/foo/child0', - siblingsCount: 3, - title: 'Child0', - type: 'article', - uid: 551503844, - }, - { - childUIDs: [ - 1438225895, - 1439149417, - 1440072939, - ], - deprecatedChildrenCount: 0, - depth: 1, - index: 0, - parent: 551503844, - siblingsCount: 4, - title: 'Group Marker, Child 0', - type: 'groupMarker', - uid: -361407047, - }, - { - childUIDs: [], - depth: 1, - groupMarkerUID: -361407047, - index: 1, - parent: 551503844, - path: '/foo/child0/grandchild0', - siblingsCount: 4, - title: 'Child0_GrandChild0', - type: 'tutorial', - uid: 1438225895, - }, - { - childUIDs: [ - 305326087, - ], - depth: 1, - groupMarkerUID: -361407047, - index: 2, - parent: 551503844, - path: '/foo/child0/grandchild1', - siblingsCount: 4, - title: 'Child0_GrandChild1', - type: 'tutorial', - uid: 1439149417, - }, - { - childUIDs: [], - depth: 2, - index: 0, - parent: 1439149417, - path: '/foo/child0/grandchild0/greatgrandchild0', - siblingsCount: 1, - title: 'Child0_GrandChild0_GreatGrandChild0', - type: 'tutorial', - uid: 305326087, - }, - { - childUIDs: [], - depth: 1, - groupMarkerUID: -361407047, - index: 3, - parent: 551503844, - path: '/foo/child0/grandchild2', - siblingsCount: 4, - title: 'Child0_GrandChild2', - type: 'tutorial', - uid: 1440072939, - }, - { - childUIDs: [ - -827353283, - ], - depth: 0, - groupMarkerUID: -196255993, - index: 2, - parent: INDEX_ROOT_KEY, - path: '/foo/child1/', - siblingsCount: 3, - title: 'Child1', - type: 'tutorial', - uid: -97593391, - }, - { - childUIDs: [], - depth: 1, - index: 0, - parent: -97593391, - path: '/foo/child1/grandchild0', - siblingsCount: 1, - title: 'Child1_GrandChild0', - type: 'method', - uid: -827353283, - }, - ]); - }); -}); diff --git a/tests/unit/components/Navigator/__snapshots__/NavigatorDataProvider.spec.js.snap b/tests/unit/components/Navigator/__snapshots__/NavigatorDataProvider.spec.js.snap deleted file mode 100644 index f65dfb359..000000000 --- a/tests/unit/components/Navigator/__snapshots__/NavigatorDataProvider.spec.js.snap +++ /dev/null @@ -1,559 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`NavigatorDataProvider counts the amount of deprecated items a groupMarker has 1`] = ` -Array [ - Object { - "childUIDs": Array [ - 365745369, - -1561138820, - ], - "deprecatedChildrenCount": 2, - "depth": 0, - "id": "Group Marker", - "index": 0, - "parent": , - "siblingsCount": 3, - "title": "Group Marker", - "type": "groupMarker", - "uid": -196255993, - }, - Object { - "childUIDs": Array [ - -1893192488, - -2046366661, - -2045443139, - -2044519617, - ], - "deprecated": true, - "depth": 0, - "groupMarkerUID": -196255993, - "id": "Child0", - "index": 1, - "parent": , - "path": "/documentation/foo/child0", - "siblingsCount": 3, - "title": "Child0", - "type": "article", - "uid": 365745369, - }, - Object { - "childUIDs": Array [ - -2046366661, - -2045443139, - -2044519617, - ], - "deprecatedChildrenCount": 1, - "depth": 1, - "id": "Group Marker, Child 0", - "index": 0, - "parent": 365745369, - "siblingsCount": 4, - "title": "Group Marker, Child 0", - "type": "groupMarker", - "uid": -1893192488, - }, - Object { - "childUIDs": Array [], - "deprecated": true, - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild0", - "index": 1, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild0", - "siblingsCount": 4, - "title": "Child0_GrandChild0", - "type": "tutorial", - "uid": -2046366661, - }, - Object { - "childUIDs": Array [ - 366459535, - ], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild1", - "index": 2, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild1", - "siblingsCount": 4, - "title": "Child0_GrandChild1", - "type": "tutorial", - "uid": -2045443139, - }, - Object { - "childUIDs": Array [], - "depth": 2, - "id": "Child0_GrandChild0_GreatGrandChild0", - "index": 0, - "parent": -2045443139, - "path": "/documentation/foo/child0/grandchild0/greatgrandchild0", - "siblingsCount": 1, - "title": "Child0_GrandChild0_GreatGrandChild0", - "type": "tutorial", - "uid": 366459535, - }, - Object { - "childUIDs": Array [], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild2", - "index": 3, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild2", - "siblingsCount": 4, - "title": "Child0_GrandChild2", - "type": "tutorial", - "uid": -2044519617, - }, - Object { - "childUIDs": Array [ - 1069111095, - ], - "deprecated": true, - "depth": 0, - "groupMarkerUID": -196255993, - "id": "Child1", - "index": 2, - "parent": , - "path": "/documentation/foo/child1/", - "siblingsCount": 3, - "title": "Child1", - "type": "tutorial", - "uid": -1561138820, - }, - Object { - "childUIDs": Array [], - "depth": 1, - "id": "Child1_GrandChild0", - "index": 0, - "parent": -1561138820, - "path": "/documentation/foo/child1/grandchild0", - "siblingsCount": 1, - "title": "Child1_GrandChild0", - "type": "method", - "uid": 1069111095, - }, -] -`; - -exports[`NavigatorDataProvider removes the \`beta\` flag from children, if the parent is a \`beta\` 1`] = ` -Object { - "apiChanges": null, - "errorFetching": false, - "flatChildren": Array [ - Object { - "childUIDs": Array [ - 365745369, - -1561138820, - ], - "deprecatedChildrenCount": 0, - "depth": 0, - "id": "Group Marker", - "index": 0, - "parent": , - "siblingsCount": 3, - "title": "Group Marker", - "type": "groupMarker", - "uid": -196255993, - }, - Object { - "beta": false, - "childUIDs": Array [ - -1893192488, - -2046366661, - -2045443139, - -2044519617, - ], - "depth": 0, - "groupMarkerUID": -196255993, - "id": "Child0", - "index": 1, - "parent": , - "path": "/documentation/foo/child0", - "siblingsCount": 3, - "title": "Child0", - "type": "article", - "uid": 365745369, - }, - Object { - "beta": false, - "childUIDs": Array [ - -2046366661, - -2045443139, - -2044519617, - ], - "deprecatedChildrenCount": 0, - "depth": 1, - "id": "Group Marker, Child 0", - "index": 0, - "parent": 365745369, - "siblingsCount": 4, - "title": "Group Marker, Child 0", - "type": "groupMarker", - "uid": -1893192488, - }, - Object { - "childUIDs": Array [], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild0", - "index": 1, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild0", - "siblingsCount": 4, - "title": "Child0_GrandChild0", - "type": "tutorial", - "uid": -2046366661, - }, - Object { - "childUIDs": Array [ - 366459535, - ], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild1", - "index": 2, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild1", - "siblingsCount": 4, - "title": "Child0_GrandChild1", - "type": "tutorial", - "uid": -2045443139, - }, - Object { - "childUIDs": Array [], - "depth": 2, - "id": "Child0_GrandChild0_GreatGrandChild0", - "index": 0, - "parent": -2045443139, - "path": "/documentation/foo/child0/grandchild0/greatgrandchild0", - "siblingsCount": 1, - "title": "Child0_GrandChild0_GreatGrandChild0", - "type": "tutorial", - "uid": 366459535, - }, - Object { - "childUIDs": Array [], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild2", - "index": 3, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild2", - "siblingsCount": 4, - "title": "Child0_GrandChild2", - "type": "tutorial", - "uid": -2044519617, - }, - Object { - "childUIDs": Array [ - 1069111095, - ], - "depth": 0, - "groupMarkerUID": -196255993, - "id": "Child1", - "index": 2, - "parent": , - "path": "/documentation/foo/child1/", - "siblingsCount": 3, - "title": "Child1", - "type": "tutorial", - "uid": -1561138820, - }, - Object { - "childUIDs": Array [], - "depth": 1, - "id": "Child1_GrandChild0", - "index": 0, - "parent": -1561138820, - "path": "/documentation/foo/child1/grandchild0", - "siblingsCount": 1, - "title": "Child1_GrandChild0", - "type": "method", - "uid": 1069111095, - }, - ], - "isFetching": false, - "isFetchingAPIChanges": false, - "references": undefined, - "technology": Object { - "beta": true, - "children": Array [ - Object { - "id": "Group Marker", - "title": "Group Marker", - "type": "groupMarker", - }, - Object { - "beta": true, - "children": Array [ - Object { - "beta": true, - "id": "Group Marker, Child 0", - "title": "Group Marker, Child 0", - "type": "groupMarker", - }, - Object { - "id": "Child0_GrandChild0", - "path": "/documentation/foo/child0/grandchild0", - "title": "Child0_GrandChild0", - "type": "tutorial", - }, - Object { - "children": Array [ - Object { - "id": "Child0_GrandChild0_GreatGrandChild0", - "path": "/documentation/foo/child0/grandchild0/greatgrandchild0", - "title": "Child0_GrandChild0_GreatGrandChild0", - "type": "tutorial", - }, - ], - "id": "Child0_GrandChild1", - "path": "/documentation/foo/child0/grandchild1", - "title": "Child0_GrandChild1", - "type": "tutorial", - }, - Object { - "id": "Child0_GrandChild2", - "path": "/documentation/foo/child0/grandchild2", - "title": "Child0_GrandChild2", - "type": "tutorial", - }, - ], - "id": "Child0", - "path": "/documentation/foo/child0", - "title": "Child0", - "type": "article", - }, - Object { - "children": Array [ - Object { - "id": "Child1_GrandChild0", - "path": "/documentation/foo/child1/grandchild0", - "title": "Child1_GrandChild0", - "type": "method", - }, - ], - "id": "Child1", - "path": "/documentation/foo/child1/", - "title": "Child1", - "type": "tutorial", - }, - ], - "path": "/documentation/foo", - }, -} -`; - -exports[`NavigatorDataProvider removes the \`beta\` flag from children, if the parent is a \`beta\` 2`] = ` -Object { - "apiChanges": null, - "errorFetching": false, - "flatChildren": Array [ - Object { - "childUIDs": Array [ - 365745369, - -1561138820, - ], - "deprecatedChildrenCount": 0, - "depth": 0, - "id": "Group Marker", - "index": 0, - "parent": , - "siblingsCount": 3, - "title": "Group Marker", - "type": "groupMarker", - "uid": -196255993, - }, - Object { - "beta": true, - "childUIDs": Array [ - -1893192488, - -2046366661, - -2045443139, - -2044519617, - ], - "depth": 0, - "groupMarkerUID": -196255993, - "id": "Child0", - "index": 1, - "parent": , - "path": "/documentation/foo/child0", - "siblingsCount": 3, - "title": "Child0", - "type": "article", - "uid": 365745369, - }, - Object { - "childUIDs": Array [ - -2046366661, - -2045443139, - -2044519617, - ], - "deprecatedChildrenCount": 0, - "depth": 1, - "id": "Group Marker, Child 0", - "index": 0, - "parent": 365745369, - "siblingsCount": 4, - "title": "Group Marker, Child 0", - "type": "groupMarker", - "uid": -1893192488, - }, - Object { - "beta": false, - "childUIDs": Array [], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild0", - "index": 1, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild0", - "siblingsCount": 4, - "title": "Child0_GrandChild0", - "type": "tutorial", - "uid": -2046366661, - }, - Object { - "childUIDs": Array [ - 366459535, - ], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild1", - "index": 2, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild1", - "siblingsCount": 4, - "title": "Child0_GrandChild1", - "type": "tutorial", - "uid": -2045443139, - }, - Object { - "beta": false, - "childUIDs": Array [], - "depth": 2, - "id": "Child0_GrandChild0_GreatGrandChild0", - "index": 0, - "parent": -2045443139, - "path": "/documentation/foo/child0/grandchild0/greatgrandchild0", - "siblingsCount": 1, - "title": "Child0_GrandChild0_GreatGrandChild0", - "type": "tutorial", - "uid": 366459535, - }, - Object { - "childUIDs": Array [], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild2", - "index": 3, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild2", - "siblingsCount": 4, - "title": "Child0_GrandChild2", - "type": "tutorial", - "uid": -2044519617, - }, - Object { - "childUIDs": Array [ - 1069111095, - ], - "depth": 0, - "groupMarkerUID": -196255993, - "id": "Child1", - "index": 2, - "parent": , - "path": "/documentation/foo/child1/", - "siblingsCount": 3, - "title": "Child1", - "type": "tutorial", - "uid": -1561138820, - }, - Object { - "beta": true, - "childUIDs": Array [], - "depth": 1, - "id": "Child1_GrandChild0", - "index": 0, - "parent": -1561138820, - "path": "/documentation/foo/child1/grandchild0", - "siblingsCount": 1, - "title": "Child1_GrandChild0", - "type": "method", - "uid": 1069111095, - }, - ], - "isFetching": false, - "isFetchingAPIChanges": false, - "references": undefined, - "technology": Object { - "children": Array [ - Object { - "id": "Group Marker", - "title": "Group Marker", - "type": "groupMarker", - }, - Object { - "beta": true, - "children": Array [ - Object { - "id": "Group Marker, Child 0", - "title": "Group Marker, Child 0", - "type": "groupMarker", - }, - Object { - "beta": true, - "id": "Child0_GrandChild0", - "path": "/documentation/foo/child0/grandchild0", - "title": "Child0_GrandChild0", - "type": "tutorial", - }, - Object { - "children": Array [ - Object { - "beta": true, - "id": "Child0_GrandChild0_GreatGrandChild0", - "path": "/documentation/foo/child0/grandchild0/greatgrandchild0", - "title": "Child0_GrandChild0_GreatGrandChild0", - "type": "tutorial", - }, - ], - "id": "Child0_GrandChild1", - "path": "/documentation/foo/child0/grandchild1", - "title": "Child0_GrandChild1", - "type": "tutorial", - }, - Object { - "id": "Child0_GrandChild2", - "path": "/documentation/foo/child0/grandchild2", - "title": "Child0_GrandChild2", - "type": "tutorial", - }, - ], - "id": "Child0", - "path": "/documentation/foo/child0", - "title": "Child0", - "type": "article", - }, - Object { - "children": Array [ - Object { - "beta": true, - "id": "Child1_GrandChild0", - "path": "/documentation/foo/child1/grandchild0", - "title": "Child1_GrandChild0", - "type": "method", - }, - ], - "id": "Child1", - "path": "/documentation/foo/child1/", - "title": "Child1", - "type": "tutorial", - }, - ], - "path": "/documentation/foo", - }, -} -`; From b6248406d54c2db25606d943d1511a5b9012d793 Mon Sep 17 00:00:00 2001 From: Hanqing Huang Date: Mon, 16 Sep 2024 15:19:51 -0700 Subject: [PATCH 3/9] `flatChildren` should default to null `flatChildren` should default to null --- src/components/DocumentationLayout.vue | 3 ++- src/stores/IndexStore.js | 4 ++-- tests/unit/mixins/indexProvider.spec.js | 2 +- tests/unit/stores/IndexStore.spec.js | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/DocumentationLayout.vue b/src/components/DocumentationLayout.vue index d6f023286..b4ba5015b 100644 --- a/src/components/DocumentationLayout.vue +++ b/src/components/DocumentationLayout.vue @@ -155,7 +155,8 @@ export default { enableQuickNavigation: ({ isTargetIDE }) => ( !isTargetIDE && getSetting(['features', 'docs', 'quickNavigation', 'enable'], true) ), - indexNodes({ indexState: { flatChildren = {} }, interfaceLanguage }) { + indexNodes({ indexState: { flatChildren }, interfaceLanguage }) { + if (!flatChildren) return []; return flatChildren[interfaceLanguage] ?? (flatChildren[Language.swift.key.url] || []); }, navigatorProps: ({ diff --git a/src/stores/IndexStore.js b/src/stores/IndexStore.js index 1b2d2ee45..36e2aeac3 100644 --- a/src/stores/IndexStore.js +++ b/src/stores/IndexStore.js @@ -10,7 +10,7 @@ export default { state: { - flatChildren: {}, + flatChildren: null, references: {}, apiChanges: null, includedArchiveIdentifiers: [], @@ -18,7 +18,7 @@ export default { technologyProps: {}, }, reset() { - this.state.flatChildren = {}; + this.state.flatChildren = null; this.state.references = {}; this.state.apiChanges = null; this.state.includedArchiveIdentifiers = []; diff --git a/tests/unit/mixins/indexProvider.spec.js b/tests/unit/mixins/indexProvider.spec.js index 6a8c30453..052db8abd 100644 --- a/tests/unit/mixins/indexProvider.spec.js +++ b/tests/unit/mixins/indexProvider.spec.js @@ -230,7 +230,7 @@ describe('indexProvider', () => { expect(fetchData).toHaveBeenCalledTimes(1); await flushPromises(); expect(IndexStore.state).toEqual({ - flatChildren: {}, + flatChildren: null, references: {}, apiChanges: null, includedArchiveIdentifiers: [], diff --git a/tests/unit/stores/IndexStore.spec.js b/tests/unit/stores/IndexStore.spec.js index 28679ad98..609f020c0 100644 --- a/tests/unit/stores/IndexStore.spec.js +++ b/tests/unit/stores/IndexStore.spec.js @@ -43,7 +43,7 @@ const technologyProps = { describe('IndexStore', () => { const defaultState = { - flatChildren: {}, + flatChildren: null, references: {}, apiChanges: null, includedArchiveIdentifiers: [], From dc1fdae10e34a04972d910c37096e7a928187ea4 Mon Sep 17 00:00:00 2001 From: Hanqing Huang Date: Mon, 16 Sep 2024 15:28:46 -0700 Subject: [PATCH 4/9] fix `isFetching` logic fix `isFetching` logic --- src/components/DocumentationLayout.vue | 2 ++ src/components/Navigator.vue | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/DocumentationLayout.vue b/src/components/DocumentationLayout.vue index b4ba5015b..c52c8c740 100644 --- a/src/components/DocumentationLayout.vue +++ b/src/components/DocumentationLayout.vue @@ -162,6 +162,7 @@ export default { navigatorProps: ({ indexNodes, indexState: { + flatChildren, references, apiChanges, errorFetching, @@ -172,6 +173,7 @@ export default { flatChildren: indexNodes, navigatorReferences: references, apiChanges, + isFetching: !flatChildren && !errorFetching, errorFetching, technologyProps: technologyProps || technology, }), diff --git a/src/components/Navigator.vue b/src/components/Navigator.vue index efc4e2739..de3de1c8e 100644 --- a/src/components/Navigator.vue +++ b/src/components/Navigator.vue @@ -95,6 +95,10 @@ export default { type: Object, required: false, }, + isFetching: { + type: Boolean, + default: false, + }, references: { type: Object, default: () => {}, @@ -121,9 +125,6 @@ export default { }, }, computed: { - isFetching({ flatChildren, errorFetching }) { - return !flatChildren.length && !errorFetching; - }, // gets the paths for each parent in the breadcrumbs parentTopicReferences({ references, parentTopicIdentifiers }) { return parentTopicIdentifiers From 9c86360951173e0fbe3862c262476069110e9570 Mon Sep 17 00:00:00 2001 From: Hanqing Huang Date: Mon, 16 Sep 2024 17:05:31 -0700 Subject: [PATCH 5/9] add/fix tests add/fix tests --- src/components/DocumentationLayout.vue | 12 +- .../components/DocumentationLayout.spec.js | 132 +++++++----------- tests/unit/components/Navigator.spec.js | 92 ++++-------- 3 files changed, 86 insertions(+), 150 deletions(-) diff --git a/src/components/DocumentationLayout.vue b/src/components/DocumentationLayout.vue index c52c8c740..4acbf5a72 100644 --- a/src/components/DocumentationLayout.vue +++ b/src/components/DocumentationLayout.vue @@ -159,6 +159,13 @@ export default { if (!flatChildren) return []; return flatChildren[interfaceLanguage] ?? (flatChildren[Language.swift.key.url] || []); }, + technologyProps({ indexState: { technologyProps }, interfaceLanguage, technology }) { + return technologyProps[interfaceLanguage] ?? (technologyProps[Language.swift.key.url] ?? { + technology: technology?.title, + technologyPath: technology?.path || technology?.url, + isTechnologyBeta: technology?.beta, + }); + }, navigatorProps: ({ indexNodes, indexState: { @@ -166,16 +173,15 @@ export default { references, apiChanges, errorFetching, - technologyProps, }, - technology, + technologyProps, }) => ({ flatChildren: indexNodes, navigatorReferences: references, apiChanges, isFetching: !flatChildren && !errorFetching, errorFetching, - technologyProps: technologyProps || technology, + technologyProps, }), sidebarProps: ({ sidenavVisibleOnMobile, enableNavigator, sidenavHiddenOnLarge, navigatorFixedWidth, diff --git a/tests/unit/components/DocumentationLayout.spec.js b/tests/unit/components/DocumentationLayout.spec.js index 30763b2b1..a90d4f6b3 100644 --- a/tests/unit/components/DocumentationLayout.spec.js +++ b/tests/unit/components/DocumentationLayout.spec.js @@ -8,14 +8,12 @@ * See https://swift.org/CONTRIBUTORS.txt for Swift project authors */ -import * as dataUtils from 'docc-render/utils/data'; import { shallowMount } from '@vue/test-utils'; import DocumentationTopicStore from 'docc-render/stores/DocumentationTopicStore'; import onPageLoadScrollToFragment from 'docc-render/mixins/onPageLoadScrollToFragment'; import DocumentationNav from 'docc-render/components/DocumentationTopic/DocumentationNav.vue'; import NavBase from 'docc-render/components/NavBase.vue'; import AdjustableSidebarWidth from '@/components/AdjustableSidebarWidth.vue'; -import NavigatorDataProvider from '@/components/Navigator/NavigatorDataProvider.vue'; import Language from '@/constants/Language'; import Navigator from '@/components/Navigator.vue'; import { storage } from '@/utils/storage'; @@ -30,37 +28,8 @@ jest.mock('docc-render/utils/scroll-lock'); jest.mock('docc-render/utils/storage'); jest.mock('docc-render/utils/theme-settings'); -const swiftChildren = [ - 'swiftChildrenMock', -]; -const objcChildren = [ - 'objcChildrenMock', -]; -jest.mock('docc-render/stores/IndexStore', () => ({ - state: { - flatChildren: { - swift: swiftChildren, - }, - }, -})); - storage.get.mockImplementation((key, value) => value); -const TechnologyWithChildren = { - path: '/documentation/foo', - children: [], -}; - -const navigatorReferences = { foo: {} }; - -jest.spyOn(dataUtils, 'fetchIndexPathsData').mockResolvedValue({ - interfaceLanguages: { - [Language.swift.key.url]: [TechnologyWithChildren], - }, - references: navigatorReferences, -}); -getSetting.mockReturnValue(false); - const { Nav, QuickNavigationModal, @@ -120,12 +89,24 @@ const AdjustableSidebarWidthSmallStub = { const stubs = { AdjustableSidebarWidth, - NavigatorDataProvider, DocumentationLayout, }; const provide = { isTargetIDE: false, store: DocumentationTopicStore }; +const navigatorReferences = { foo: {} }; +const technologyProps = { + technology: references['topic://foo'].title, + technologyPath: references['topic://foo'].url, + isTechnologyBeta: references['topic://foo'].beta, +}; +const swiftChildren = [ + 'swiftChildrenMock', +]; +const objcChildren = [ + 'objcChildrenMock', +]; + const createWrapper = props => shallowMount(DocumentationLayout, { propsData, stubs, @@ -136,6 +117,17 @@ const createWrapper = props => shallowMount(DocumentationLayout, { content: '
Content
', title: '
Title
', }, + data: () => ({ + indexState: { + flatChildren: { + swift: swiftChildren, + }, + errorFetching: false, + references: navigatorReferences, + technologyProps, + apiChanges: null, + }, + }), }); describe('DocumentationLayout', () => { @@ -174,45 +166,23 @@ describe('DocumentationLayout', () => { enableNavigator: true, fixedWidth: propsData.navigatorFixedWidth, }); - const { - technology, - parentTopicIdentifiers, - } = propsData; - expect(wrapper.find(NavigatorDataProvider).props()).toEqual({ - interfaceLanguage: Language.swift.key.url, - technologyUrl: technology.url, - apiChangesVersion: '', - }); + const { parentTopicIdentifiers } = propsData; + // its rendered by default const navigator = wrapper.find(Navigator); expect(navigator.exists()).toBe(true); expect(navigator.props()).toEqual({ errorFetching: false, - isFetching: true, + isFetching: false, // assert we are passing the first set of paths always parentTopicIdentifiers, references, scrollLockID: AdjustableSidebarWidth.constants.SCROLL_LOCK_ID, - // assert we are passing the default technology, if we dont have the children yet - technology, - apiChanges: null, - flatChildren: [], - navigatorReferences: {}, - renderFilterOnTop: false, - }); - expect(dataUtils.fetchIndexPathsData).toHaveBeenCalledTimes(1); - await flushPromises(); - expect(navigator.props()).toEqual({ - errorFetching: false, - isFetching: false, - scrollLockID: AdjustableSidebarWidth.constants.SCROLL_LOCK_ID, - renderFilterOnTop: false, - parentTopicIdentifiers, - references, - technology: TechnologyWithChildren, + technologyProps, apiChanges: null, - flatChildren: [], + flatChildren: swiftChildren, navigatorReferences, + renderFilterOnTop: false, }); // assert the nav is in wide format const nav = wrapper.find(Nav); @@ -238,6 +208,7 @@ describe('DocumentationLayout', () => { }); it('does not render QuickNavigation if enableQuickNavigation is false', () => { + getSetting.mockReturnValueOnce(false); wrapper = createWrapper({ stubs: { ...stubs, @@ -350,7 +321,6 @@ describe('DocumentationLayout', () => { wrapper = createWrapper({ stubs: { AdjustableSidebarWidth: AdjustableSidebarWidthSmallStub, - NavigatorDataProvider, }, }); }); @@ -389,14 +359,14 @@ describe('DocumentationLayout', () => { }); }); - it('provides the selected api changes, to the NavigatorDataProvider', () => { - wrapper.setProps({ - enableNavigator: true, - selectedAPIChangesVersion: 'latest_major', - }); - const dataProvider = wrapper.find(NavigatorDataProvider); - expect(dataProvider.props('apiChangesVersion')).toEqual('latest_major'); - }); + // it('provides the selected api changes, to the NavigatorDataProvider', () => { + // wrapper.setProps({ + // enableNavigator: true, + // selectedAPIChangesVersion: 'latest_major', + // }); + // const dataProvider = wrapper.find(NavigatorDataProvider); + // expect(dataProvider.props('apiChangesVersion')).toEqual('latest_major'); + // }); it('renders the Navigator with data when no reference is found for a top-level item', () => { const technologies = { @@ -417,9 +387,10 @@ describe('DocumentationLayout', () => { const navigator = wrapper.find(Navigator); expect(navigator.exists()).toBe(true); // assert the technology is the last fallback - expect(navigator.props('technology')).toEqual({ - title: 'FooTechnology', - url: '/documentation/foo', + expect(navigator.props('technologyProps')).toEqual({ + technology: 'FooTechnology', + technologyPath: '/documentation/foo', + isTechnologyBeta: undefined, }); }); @@ -444,9 +415,10 @@ describe('DocumentationLayout', () => { const navigator = wrapper.find(Navigator); expect(navigator.exists()).toBe(true); // assert the technology is the last fallback - expect(navigator.props('technology')).toEqual({ - title: 'FooTechnology', - url: '/documentation/foo', + expect(navigator.props('technologyProps')).toEqual({ + technology: 'FooTechnology', + technologyPath: '/documentation/foo', + isTechnologyBeta: undefined, }); }); @@ -460,7 +432,11 @@ describe('DocumentationLayout', () => { const navigator = wrapper.find(Navigator); expect(navigator.exists()).toBe(true); // assert the technology is the last fallback - expect(navigator.props('technology')).toEqual(propsData.technology); + expect(navigator.props('technologyProps')).toEqual({ + technology: propsData.technology.title, + technologyPath: propsData.technology.url, + isTechnologyBeta: propsData.technology.beta, + }); }); it('renders without a sidebar', () => { @@ -490,10 +466,6 @@ describe('DocumentationLayout', () => { .toEqual(expect.arrayContaining(['topic-wrapper', 'full-width-container'])); }); - it('renders without NavigatorDataProvider', async () => { - expect(wrapper.find(NavigatorDataProvider).exists()).toBe(false); - }); - it('handles the `@close`, on Navigator, for Mobile breakpoints', async () => { wrapper.setProps({ enableNavigator: true, diff --git a/tests/unit/components/Navigator.spec.js b/tests/unit/components/Navigator.spec.js index fbfc42f94..34ec65097 100644 --- a/tests/unit/components/Navigator.spec.js +++ b/tests/unit/components/Navigator.spec.js @@ -19,56 +19,9 @@ jest.mock('docc-render/utils/throttle', () => jest.fn(v => v)); const { LoadingNavigatorCard } = Navigator.components; -const technology = { - title: 'FooTechnology', - children: [ - { - title: 'Group Marker', - type: TopicTypes.groupMarker, - }, - { - title: 'Child0', - path: '/foo/child0', - type: 'article', - children: [ - { - title: 'Group Marker, Child 0', - type: TopicTypes.groupMarker, - }, - { - title: 'Child0_GrandChild0', - path: '/foo/child0/grandchild0', - type: 'tutorial', - }, - { - title: 'Child0_GrandChild1', - path: '/foo/child0/grandchild1', - type: 'tutorial', - children: [{ - title: 'Child0_GrandChild0_GreatGrandChild0', - path: '/foo/child0/grandchild0/greatgrandchild0', - type: 'tutorial', - }], - }, - { - title: 'Child0_GrandChild2', - path: '/foo/child0/grandchild2', - type: 'tutorial', - }, - ], - }, - { - title: 'Child1', - path: '/foo/child1/', - type: 'tutorial', - children: [{ - title: 'Child1_GrandChild0', - path: '/foo/child1/grandchild0', - type: 'method', - }], - }, - ], - path: '/path/to/technology', +const technologyProps = { + technology: 'FooTechnology', + technologyPath: '/path/to/technology', }; const references = { @@ -100,7 +53,7 @@ const navigatorReferences = { const defaultProps = { parentTopicIdentifiers, - technology, + technologyProps, references, scrollLockID: 'foo', renderFilterOnTop: false, @@ -141,8 +94,8 @@ describe('Navigator', () => { // will assert in another test children: [], type: TopicTypes.module, - technology: technology.title, - technologyPath: technology.path, + technology: technologyProps.technology, + technologyPath: technologyProps.technologyPath, isTechnologyBeta: false, scrollLockID: defaultProps.scrollLockID, renderFilterOnTop: defaultProps.renderFilterOnTop, @@ -153,16 +106,6 @@ describe('Navigator', () => { }); }); - it('renders an aria live that tells VO users when navigator is loading', () => { - const wrapper = createWrapper({ - propsData: { - isFetching: true, - }, - }); - expect(wrapper.find('[aria-live="polite"]').exists()).toBe(true); - expect(wrapper.find('[aria-live="polite"]').text()).toBe('navigator.navigator-is navigator.state.loading'); - }); - it('renders a LoadingNavigatorCard when navigator is loading', () => { const wrapper = createWrapper({ propsData: { @@ -186,6 +129,16 @@ describe('Navigator', () => { expect(wrapper.find(NavigatorCard).attributes('style')).toContain('display: none'); }); + it('renders an aria live that tells VO users when navigator is loading', () => { + const wrapper = createWrapper({ + propsData: { + isFetching: true, + }, + }); + expect(wrapper.find('[aria-live="polite"]').exists()).toBe(true); + expect(wrapper.find('[aria-live="polite"]').text()).toBe('navigator.navigator-is navigator.state.loading'); + }); + it('renders an aria live that tells VO users when navigator is ready', () => { const wrapper = createWrapper(); expect(wrapper.find('[aria-live="polite"]').exists()).toBe(true); @@ -193,9 +146,14 @@ describe('Navigator', () => { }); it('falls back to using the `technology.url` for the `technology-path`', () => { + const fallbackProps = { + technology: fallbackTechnology.title, + technologyPath: fallbackTechnology.url, + isTechnologyBeta: false, + }; const wrapper = createWrapper({ propsData: { - technology: fallbackTechnology, + technologyProps: fallbackProps, }, }); expect(wrapper.find(NavigatorCard).props()).toEqual({ @@ -203,9 +161,9 @@ describe('Navigator', () => { // will assert in another test children: [], type: TopicTypes.module, - technology: fallbackTechnology.title, - technologyPath: fallbackTechnology.url, - isTechnologyBeta: false, + technology: fallbackProps.technology, + technologyPath: fallbackProps.technologyPath, + isTechnologyBeta: fallbackProps.isTechnologyBeta, scrollLockID: defaultProps.scrollLockID, renderFilterOnTop: defaultProps.renderFilterOnTop, errorFetching: false, From ed8dca385be7595b0089abb7d7dc28becb882ad5 Mon Sep 17 00:00:00 2001 From: Hanqing Huang Date: Mon, 16 Sep 2024 17:14:19 -0700 Subject: [PATCH 6/9] clean up `getSetting` mock return value clean up `getSetting` mock return value --- .../unit/components/DocumentationLayout.spec.js | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/tests/unit/components/DocumentationLayout.spec.js b/tests/unit/components/DocumentationLayout.spec.js index a90d4f6b3..f58791621 100644 --- a/tests/unit/components/DocumentationLayout.spec.js +++ b/tests/unit/components/DocumentationLayout.spec.js @@ -29,6 +29,7 @@ jest.mock('docc-render/utils/storage'); jest.mock('docc-render/utils/theme-settings'); storage.get.mockImplementation((key, value) => value); +getSetting.mockReturnValue(true); const { Nav, @@ -190,7 +191,6 @@ describe('DocumentationLayout', () => { }); it('renders QuickNavigation if enableQuickNavigation is true', () => { - getSetting.mockReturnValueOnce(true); wrapper = createWrapper({ stubs: { ...stubs, @@ -226,7 +226,6 @@ describe('DocumentationLayout', () => { }); it('does not render QuickNavigation and MagnifierIcon if enableNavigation is false', () => { - getSetting.mockReturnValueOnce(true); wrapper = createWrapper({ stubs: { ...stubs, @@ -240,7 +239,6 @@ describe('DocumentationLayout', () => { }); it('does not render QuickNavigation if enableQuickNavigation is true but IDE is being targeted', () => { - getSetting.mockReturnValueOnce(true); wrapper = createWrapper({ provide: { ...provide, isTargetIDE: true }, stubs: { @@ -259,7 +257,6 @@ describe('DocumentationLayout', () => { }); it('QuickNavigation renders Swift items', async () => { - getSetting.mockReturnValueOnce(true); wrapper = createWrapper({ stubs: { ...stubs, @@ -275,7 +272,6 @@ describe('DocumentationLayout', () => { }); it('QuickNavigation falls back to swift items, if no objc items', async () => { - getSetting.mockReturnValueOnce(true); wrapper = createWrapper({ stubs: { ...stubs, @@ -292,7 +288,6 @@ describe('DocumentationLayout', () => { }); it('QuickNavigation renders objc items', async () => { - getSetting.mockReturnValueOnce(true); wrapper = createWrapper({ stubs: { ...stubs, @@ -359,15 +354,6 @@ describe('DocumentationLayout', () => { }); }); - // it('provides the selected api changes, to the NavigatorDataProvider', () => { - // wrapper.setProps({ - // enableNavigator: true, - // selectedAPIChangesVersion: 'latest_major', - // }); - // const dataProvider = wrapper.find(NavigatorDataProvider); - // expect(dataProvider.props('apiChangesVersion')).toEqual('latest_major'); - // }); - it('renders the Navigator with data when no reference is found for a top-level item', () => { const technologies = { id: 'topic://not-existing', From 728a17197c31d9b567ef4d98661d45fcae8bf403 Mon Sep 17 00:00:00 2001 From: Hanqing Huang Date: Tue, 17 Sep 2024 13:10:01 -0700 Subject: [PATCH 7/9] select correct variant for `technologyProp` select correct variant for `technologyProp` --- src/components/DocumentationLayout.vue | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/DocumentationLayout.vue b/src/components/DocumentationLayout.vue index 4acbf5a72..3729f2d40 100644 --- a/src/components/DocumentationLayout.vue +++ b/src/components/DocumentationLayout.vue @@ -160,11 +160,14 @@ export default { return flatChildren[interfaceLanguage] ?? (flatChildren[Language.swift.key.url] || []); }, technologyProps({ indexState: { technologyProps }, interfaceLanguage, technology }) { - return technologyProps[interfaceLanguage] ?? (technologyProps[Language.swift.key.url] ?? { - technology: technology?.title, - technologyPath: technology?.path || technology?.url, - isTechnologyBeta: technology?.beta, - }); + // Select technology props from fetched index data for the current language, fallback to swift + // If none available, fallback to technology data of the curr page or null + return technologyProps[interfaceLanguage] ?? technologyProps[Language.swift.key.url] + ?? (technology ? { + technology: technology?.title, + technologyPath: technology?.path || technology?.url, + isTechnologyBeta: technology?.beta, + } : null); }, navigatorProps: ({ indexNodes, From 5a21114e42f963eeb49d689568fab7dc1cbd987c Mon Sep 17 00:00:00 2001 From: Hanqing Huang Date: Tue, 17 Sep 2024 13:18:13 -0700 Subject: [PATCH 8/9] create mixin for getting data from the store create mixin for getting data from the store --- src/components/DocumentationLayout.vue | 38 +------------------ src/mixins/indexGetter.js | 52 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 36 deletions(-) create mode 100644 src/mixins/indexGetter.js diff --git a/src/components/DocumentationLayout.vue b/src/components/DocumentationLayout.vue index 3729f2d40..ae4383f8f 100644 --- a/src/components/DocumentationLayout.vue +++ b/src/components/DocumentationLayout.vue @@ -81,12 +81,10 @@ import QuickNavigationModal from 'docc-render/components/Navigator/QuickNavigati import AdjustableSidebarWidth from 'docc-render/components/AdjustableSidebarWidth.vue'; import Navigator from 'docc-render/components/Navigator.vue'; import onPageLoadScrollToFragment from 'docc-render/mixins/onPageLoadScrollToFragment'; -import Language from 'docc-render/constants/Language'; import { BreakpointName } from 'docc-render/utils/breakpoints'; import { storage } from 'docc-render/utils/storage'; import { getSetting } from 'docc-render/utils/theme-settings'; - -import IndexStore from 'docc-render/stores/IndexStore'; +import indexGetter from 'docc-render/mixins/indexGetter'; import DocumentationNav from 'theme/components/DocumentationTopic/DocumentationNav.vue'; const NAVIGATOR_HIDDEN_ON_LARGE_KEY = 'navigator-hidden-large'; @@ -102,7 +100,7 @@ export default { QuickNavigationModal, PortalTarget, }, - mixins: [onPageLoadScrollToFragment], + mixins: [onPageLoadScrollToFragment, indexGetter], props: { enableNavigator: Boolean, diffAvailability: { @@ -148,44 +146,12 @@ export default { sidenavHiddenOnLarge: storage.get(NAVIGATOR_HIDDEN_ON_LARGE_KEY, false), showQuickNavigationModal: false, BreakpointName, - indexState: IndexStore.state, }; }, computed: { enableQuickNavigation: ({ isTargetIDE }) => ( !isTargetIDE && getSetting(['features', 'docs', 'quickNavigation', 'enable'], true) ), - indexNodes({ indexState: { flatChildren }, interfaceLanguage }) { - if (!flatChildren) return []; - return flatChildren[interfaceLanguage] ?? (flatChildren[Language.swift.key.url] || []); - }, - technologyProps({ indexState: { technologyProps }, interfaceLanguage, technology }) { - // Select technology props from fetched index data for the current language, fallback to swift - // If none available, fallback to technology data of the curr page or null - return technologyProps[interfaceLanguage] ?? technologyProps[Language.swift.key.url] - ?? (technology ? { - technology: technology?.title, - technologyPath: technology?.path || technology?.url, - isTechnologyBeta: technology?.beta, - } : null); - }, - navigatorProps: ({ - indexNodes, - indexState: { - flatChildren, - references, - apiChanges, - errorFetching, - }, - technologyProps, - }) => ({ - flatChildren: indexNodes, - navigatorReferences: references, - apiChanges, - isFetching: !flatChildren && !errorFetching, - errorFetching, - technologyProps, - }), sidebarProps: ({ sidenavVisibleOnMobile, enableNavigator, sidenavHiddenOnLarge, navigatorFixedWidth, }) => ( diff --git a/src/mixins/indexGetter.js b/src/mixins/indexGetter.js new file mode 100644 index 000000000..0d0f47d9c --- /dev/null +++ b/src/mixins/indexGetter.js @@ -0,0 +1,52 @@ +/** + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2024 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors +*/ +import IndexStore from 'docc-render/stores/IndexStore'; +import Language from 'docc-render/constants/Language'; + +export default { + computed: { + indexNodes({ indexState: { flatChildren }, interfaceLanguage }) { + if (!flatChildren) return []; + return flatChildren[interfaceLanguage] ?? (flatChildren[Language.swift.key.url] || []); + }, + technologyProps({ indexState: { technologyProps }, interfaceLanguage, technology }) { + // Select technology props from fetched index data for the current language, fallback to swift + // If none available, fallback to technology data of the curr page or null + return technologyProps[interfaceLanguage] ?? technologyProps[Language.swift.key.url] + ?? (technology ? { + technology: technology?.title, + technologyPath: technology?.path || technology?.url, + isTechnologyBeta: technology?.beta, + } : null); + }, + navigatorProps: ({ + indexNodes, + indexState: { + flatChildren, + references, + apiChanges, + errorFetching, + }, + technologyProps, + }) => ({ + flatChildren: indexNodes, + navigatorReferences: references, + apiChanges, + isFetching: !flatChildren && !errorFetching, + errorFetching, + technologyProps, + }), + }, + data() { + return { + indexState: IndexStore.state, + }; + }, +}; From 889166c362663e792b6e2ee07e979f39561aba00 Mon Sep 17 00:00:00 2001 From: Hanqing Huang Date: Wed, 18 Sep 2024 14:45:53 -0700 Subject: [PATCH 9/9] minor refinements minor refinements --- src/mixins/indexGetter.js | 6 +++--- tests/unit/stores/IndexStore.spec.js | 20 ++++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/mixins/indexGetter.js b/src/mixins/indexGetter.js index 0d0f47d9c..11eefa7b4 100644 --- a/src/mixins/indexGetter.js +++ b/src/mixins/indexGetter.js @@ -21,9 +21,9 @@ export default { // If none available, fallback to technology data of the curr page or null return technologyProps[interfaceLanguage] ?? technologyProps[Language.swift.key.url] ?? (technology ? { - technology: technology?.title, - technologyPath: technology?.path || technology?.url, - isTechnologyBeta: technology?.beta, + technology: technology.title, + technologyPath: technology.path || technology.url, + isTechnologyBeta: technology.beta, } : null); }, navigatorProps: ({ diff --git a/tests/unit/stores/IndexStore.spec.js b/tests/unit/stores/IndexStore.spec.js index 609f020c0..365af0f2e 100644 --- a/tests/unit/stores/IndexStore.spec.js +++ b/tests/unit/stores/IndexStore.spec.js @@ -11,14 +11,18 @@ import IndexStore from 'docc-render/stores/IndexStore'; import Language from 'docc-render/constants/Language'; -const flatChildren = [ - { - title: 'item 1', - }, - { - title: 'item 2', - }, -]; +const flatChildren = { + [Language.swift.key.url]: [ + { + title: 'item 1', + }, + ], + [Language.objectiveC.key.url]: [ + { + title: 'item 2', + }, + ], +}; const references = { foo: { identifier: 'foo' },