Skip to content

Creating an IndexStore for Quick Navigation #889

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

Merged
merged 27 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d2bd931
Create `IndexStore`
hqhhuang Aug 12, 2024
6fce256
fetch index data in `view/DocumentationTopic`
hqhhuang Aug 13, 2024
642f741
Get Quick Nav data from `IndexStore`
hqhhuang Aug 13, 2024
c9899cb
update items in `indexStore`
hqhhuang Aug 13, 2024
00131f7
add `technologyUrl` prop
hqhhuang Aug 13, 2024
73db182
fix issues with accessing null items
hqhhuang Aug 19, 2024
328591c
move data fetching logic to `indexProvider.js`
hqhhuang Aug 19, 2024
0addf8a
add `errorFetching` to store
hqhhuang Aug 19, 2024
235fd8e
simplify index data path computation logic
hqhhuang Aug 22, 2024
caeb397
add technology prop to store
hqhhuang Aug 22, 2024
cf0ba56
add tests for `indexProvider`
hqhhuang Aug 23, 2024
5e2815a
Merge branch 'main' into index-store
hqhhuang Aug 23, 2024
6491aa9
resets store before fetching new indexData
hqhhuang Aug 26, 2024
9942643
do not set `includedArchiveIdentifiers` in Navigator Provider
hqhhuang Sep 3, 2024
9f7fc45
rename state to `indexState`
hqhhuang Sep 3, 2024
801593a
simplify logic to find technology index data
hqhhuang Sep 5, 2024
a1fd338
remove url related computed properties
hqhhuang Sep 9, 2024
1b8ee36
move fetching logic to `view/DocumentationTopic`
hqhhuang Sep 9, 2024
835ad6b
display index data for correct language variant
hqhhuang Sep 9, 2024
cbb7f50
save flattened data for all language variants to store
hqhhuang Sep 9, 2024
3dfcb67
Merge branch 'index-store' of github.com:hqhhuang/swift-docc-render i…
hqhhuang Sep 9, 2024
6fa85b6
Revert "Merge branch 'index-store' of github.com:hqhhuang/swift-docc-…
hqhhuang Sep 9, 2024
0e92cc1
update flatChildren default format
hqhhuang Sep 9, 2024
b8cec41
make sure data defaults to swift variant & add tests
hqhhuang Sep 10, 2024
24519c6
rename and clean up node logic
hqhhuang Sep 11, 2024
8aa6337
Merge branch 'main' into index-store
hqhhuang Sep 11, 2024
5026c9e
Improve tests and test description
hqhhuang Sep 11, 2024
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
8 changes: 7 additions & 1 deletion src/components/DocumentationLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
<div class="documentation-layout-aside">
<QuickNavigationModal
v-if="enableQuickNavigation"
:children="slotProps.flatChildren"
:children="quickNavNodes"
:showQuickNavigationModal.sync="showQuickNavigationModal"
:technology="technology ? technology.title : ''"
/>
Expand Down Expand Up @@ -83,10 +83,12 @@ 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 NavigatorDataProvider from 'theme/components/Navigator/NavigatorDataProvider.vue';
import DocumentationNav from 'theme/components/DocumentationTopic/DocumentationNav.vue';

Expand Down Expand Up @@ -150,12 +152,16 @@ 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)
),
quickNavNodes({ indexState: { flatChildren = {} }, interfaceLanguage }) {
return flatChildren[interfaceLanguage] ?? (flatChildren[Language.swift.key.url] || []);
},
sidebarProps: ({
sidenavVisibleOnMobile, enableNavigator, sidenavHiddenOnLarge, navigatorFixedWidth,
}) => (
Expand Down
3 changes: 0 additions & 3 deletions src/components/Navigator/NavigatorDataProvider.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
<script>
import { fetchIndexPathsData } from 'docc-render/utils/data';
import { flattenNestedData } from 'docc-render/utils/navigatorData';
import AppStore from 'docc-render/stores/AppStore';
import Language from 'docc-render/constants/Language';

/**
Expand Down Expand Up @@ -91,15 +90,13 @@ export default {
try {
this.isFetching = true;
const {
includedArchiveIdentifiers = [],
interfaceLanguages,
references,
} = await fetchIndexPathsData(
{ slug: this.$route.params.locale || '' },
);
this.navigationIndex = Object.freeze(interfaceLanguages);
this.navigationReferences = Object.freeze(references);
AppStore.setIncludedArchiveIdentifiers(includedArchiveIdentifiers);
} catch (e) {
this.errorFetching = true;
} finally {
Expand Down
49 changes: 49 additions & 0 deletions src/mixins/indexProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* 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 { fetchData } from 'docc-render/utils/data';
import { pathJoin } from 'docc-render/utils/assets';
import { flattenNavigationIndex, extractTechnologyProps } from 'docc-render/utils/navigatorData';
import IndexStore from 'docc-render/stores/IndexStore';

export default {
computed: {
indexDataPath() {
const slug = this.$route?.params?.locale || '';
return pathJoin(['/index/', slug, 'index.json']);
},
},
methods: {
async fetchIndexPathsData() {
return fetchData(this.indexDataPath);
},
async fetchIndexData() {
try {
IndexStore.reset();
const {
includedArchiveIdentifiers = [],
interfaceLanguages,
references = {},
} = await this.fetchIndexPathsData();
IndexStore.setFlatChildren(flattenNavigationIndex(interfaceLanguages));
IndexStore.setTechnologyProps(extractTechnologyProps(interfaceLanguages));
IndexStore.setReferences(references);
IndexStore.setIncludedArchiveIdentifiers(includedArchiveIdentifiers);
} catch (e) {
IndexStore.setErrorFetching(true);
}
},
},
watch: {
indexDataPath: {
handler: 'fetchIndexData',
immediate: true,
},
},
};
5 changes: 0 additions & 5 deletions src/stores/AppStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,13 @@ export default {
supportsAutoColorScheme,
systemColorScheme: ColorScheme.light,
availableLocales: [],
includedArchiveIdentifiers: [],
},
reset() {
this.state.imageLoadingStrategy = process.env.VUE_APP_TARGET === 'ide'
? ImageLoadingStrategy.eager : ImageLoadingStrategy.lazy;
this.state.preferredColorScheme = Settings.preferredColorScheme || defaultColorScheme;
this.state.supportsAutoColorScheme = supportsAutoColorScheme;
this.state.systemColorScheme = ColorScheme.light;
this.state.includedArchiveIdentifiers = [];
},
setImageLoadingStrategy(strategy) {
this.state.imageLoadingStrategy = strategy;
Expand All @@ -61,9 +59,6 @@ export default {
setSystemColorScheme(value) {
this.state.systemColorScheme = value;
},
setIncludedArchiveIdentifiers(value) {
this.state.includedArchiveIdentifiers = value;
},
syncPreferredColorScheme() {
if (!!Settings.preferredColorScheme
&& Settings.preferredColorScheme !== this.state.preferredColorScheme) {
Expand Down
46 changes: 46 additions & 0 deletions src/stores/IndexStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* 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
*/

export default {
state: {
flatChildren: {},
references: {},
apiChanges: null,
includedArchiveIdentifiers: [],
errorFetching: false,
technologyProps: {},
},
reset() {
this.state.flatChildren = {};
this.state.references = {};
this.state.apiChanges = null;
this.state.includedArchiveIdentifiers = [];
this.state.errorFetching = false;
this.state.technologyProps = {};
},
setFlatChildren(children) {
this.state.flatChildren = children;
},
setReferences(references) {
this.state.references = references;
},
setApiChanges(diff) {
this.state.apiChanges = diff;
},
setIncludedArchiveIdentifiers(includedArchiveIdentifiers) {
this.state.includedArchiveIdentifiers = includedArchiveIdentifiers;
},
setErrorFetching(state) {
this.state.errorFetching = state;
},
setTechnologyProps(technology) {
this.state.technologyProps = technology;
},
};
27 changes: 27 additions & 0 deletions src/utils/navigatorData.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,30 @@ export function getSiblings(uid, childrenMap, children) {
if (!item) return [];
return getChildren(item.parent, childrenMap, children);
}

/**
* Flatten data for each language variant
* @return { languageVariant: NavigatorFlatItem[] }
*/
export function flattenNavigationIndex(indexData) {
return Object.entries(indexData).reduce((acc, [language, data]) => {
acc[language] = flattenNestedData(
data[0].children || [], null, 0, data[0].beta,
);
return acc;
}, {});
}

/**
* Extract technology data for each language variant
*/
export function extractTechnologyProps(indexData) {
return Object.entries(indexData).reduce((acc, [language, data]) => {
acc[language] = {
technology: data[0].title,
technologyPath: data[0].path || data[0].url,
isTechnologyBeta: data[0].beta,
};
return acc;
}, {});
}
3 changes: 2 additions & 1 deletion src/views/DocumentationTopic.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import {
import DocumentationTopic from 'theme/components/DocumentationTopic.vue';
import DocumentationLayout from 'docc-render/components/DocumentationLayout.vue';
import DocumentationTopicStore from 'docc-render/stores/DocumentationTopicStore';
import indexProvider from 'theme/mixins/indexProvider';
import Language from 'docc-render/constants/Language';
import OnThisPageRegistrator from 'docc-render/mixins/onThisPageRegistrator';
import { updateLocale } from 'theme/utils/i18n-utils';
Expand All @@ -74,7 +75,7 @@ export default {
Topic: DocumentationTopic,
DocumentationLayout,
},
mixins: [OnThisPageRegistrator, communicationBridgeUtils],
mixins: [OnThisPageRegistrator, communicationBridgeUtils, indexProvider],
props: {
enableMinimized: {
type: Boolean,
Expand Down
72 changes: 72 additions & 0 deletions tests/unit/components/DocumentationLayout.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ 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 = {
Expand Down Expand Up @@ -273,6 +287,64 @@ describe('DocumentationLayout', () => {
expect(quickNavigationModalComponent.exists()).toBe(false);
});

it('QuickNavigation renders Swift items', async () => {
getSetting.mockReturnValueOnce(true);
wrapper = createWrapper({
stubs: {
...stubs,
Nav: DocumentationNav,
NavBase,
},
});
wrapper.setProps({
enableNavigator: true,
});
await wrapper.vm.$nextTick();
expect(wrapper.find(QuickNavigationModal).props('children')).toEqual(swiftChildren);
});

it('QuickNavigation falls back to swift items, if no objc items', async () => {
Copy link
Member

Choose a reason for hiding this comment

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

Why do we fallback to swift items? just curious about this

Copy link
Contributor Author

@hqhhuang hqhhuang Sep 16, 2024

Choose a reason for hiding this comment

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

See this issue, DocC defaults to swift when the archive is article only. If this issue is addressed, we can make the update here as well.

getSetting.mockReturnValueOnce(true);
wrapper = createWrapper({
stubs: {
...stubs,
Nav: DocumentationNav,
NavBase,
},
});
wrapper.setProps({
enableNavigator: true,
interfaceLanguage: Language.objectiveC.key.url,
});
await wrapper.vm.$nextTick();
expect(wrapper.find(QuickNavigationModal).props('children')).toEqual(swiftChildren);
});

it('QuickNavigation renders objc items', async () => {
getSetting.mockReturnValueOnce(true);
wrapper = createWrapper({
stubs: {
...stubs,
Nav: DocumentationNav,
NavBase,
},
});
wrapper.setProps({
enableNavigator: true,
interfaceLanguage: Language.objectiveC.key.url,
});
wrapper.setData({
indexState: {
flatChildren: {
[Language.swift.key.url]: swiftChildren,
[Language.objectiveC.key.url]: objcChildren,
},
},
});
await wrapper.vm.$nextTick();
expect(wrapper.find(QuickNavigationModal).props('children')).toEqual(objcChildren);
});

describe('if breakpoint is small', () => {
beforeEach(() => {
wrapper = createWrapper({
Expand Down
13 changes: 0 additions & 13 deletions tests/unit/components/Navigator/NavigatorDataProvider.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import NavigatorDataProvider from '@/components/Navigator/NavigatorDataProvider.vue';
import { shallowMount } from '@vue/test-utils';
import AppStore from 'docc-render/stores/AppStore';
import Language from 'docc-render/constants/Language';
import { TopicTypes } from '@/constants/TopicTypes';
import { fetchIndexPathsData } from '@/utils/data';
Expand Down Expand Up @@ -175,10 +174,6 @@ describe('NavigatorDataProvider', () => {
jest.clearAllMocks();
});

afterEach(() => {
AppStore.reset();
});

it('fetches data when mounting NavigatorDataProvider', async () => {
expect(fetchIndexPathsData).toHaveBeenCalledTimes(0);
createWrapper();
Expand Down Expand Up @@ -559,12 +554,4 @@ describe('NavigatorDataProvider', () => {
},
]);
});

it('sets `includedArchiveIdentifiers` state in the app store', async () => {
expect(AppStore.state.includedArchiveIdentifiers).toEqual([]);
fetchIndexPathsData.mockResolvedValue(response);
createWrapper();
await flushPromises();
expect(AppStore.state.includedArchiveIdentifiers).toEqual(includedArchiveIdentifiers);
});
});
Loading