Skip to content

Commit 97c6c69

Browse files
authored
Create a mixin for the index data getting logic (#895) rdar://136089361
Create a mixin for the index data getting logic (#895) rdar://136089361
1 parent 0a8290f commit 97c6c69

File tree

9 files changed

+257
-29
lines changed

9 files changed

+257
-29
lines changed

src/components/DocumentationLayout.vue

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
<div class="documentation-layout-aside">
4141
<QuickNavigationModal
4242
v-if="enableQuickNavigation"
43-
:children="quickNavNodes"
43+
:children="indexNodes"
4444
:showQuickNavigationModal.sync="showQuickNavigationModal"
4545
:technology="technology ? technology.title : ''"
4646
/>
@@ -83,12 +83,11 @@ import QuickNavigationModal from 'docc-render/components/Navigator/QuickNavigati
8383
import AdjustableSidebarWidth from 'docc-render/components/AdjustableSidebarWidth.vue';
8484
import Navigator from 'docc-render/components/Navigator.vue';
8585
import onPageLoadScrollToFragment from 'docc-render/mixins/onPageLoadScrollToFragment';
86-
import Language from 'docc-render/constants/Language';
8786
import { BreakpointName } from 'docc-render/utils/breakpoints';
8887
import { storage } from 'docc-render/utils/storage';
8988
import { getSetting } from 'docc-render/utils/theme-settings';
9089
91-
import IndexStore from 'docc-render/stores/IndexStore';
90+
import indexDataGetter from 'docc-render/mixins/indexDataGetter';
9291
import NavigatorDataProvider from 'theme/components/Navigator/NavigatorDataProvider.vue';
9392
import DocumentationNav from 'theme/components/DocumentationTopic/DocumentationNav.vue';
9493
@@ -106,7 +105,7 @@ export default {
106105
QuickNavigationModal,
107106
PortalTarget,
108107
},
109-
mixins: [onPageLoadScrollToFragment],
108+
mixins: [onPageLoadScrollToFragment, indexDataGetter],
110109
props: {
111110
enableNavigator: Boolean,
112111
diffAvailability: {
@@ -152,16 +151,12 @@ export default {
152151
sidenavHiddenOnLarge: storage.get(NAVIGATOR_HIDDEN_ON_LARGE_KEY, false),
153152
showQuickNavigationModal: false,
154153
BreakpointName,
155-
indexState: IndexStore.state,
156154
};
157155
},
158156
computed: {
159157
enableQuickNavigation: ({ isTargetIDE }) => (
160158
!isTargetIDE && getSetting(['features', 'docs', 'quickNavigation', 'enable'], true)
161159
),
162-
quickNavNodes({ indexState: { flatChildren = {} }, interfaceLanguage }) {
163-
return flatChildren[interfaceLanguage] ?? (flatChildren[Language.swift.key.url] || []);
164-
},
165160
sidebarProps: ({
166161
sidenavVisibleOnMobile, enableNavigator, sidenavHiddenOnLarge, navigatorFixedWidth,
167162
}) => (
File renamed without changes.

src/mixins/indexDataGetter.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* This source file is part of the Swift.org open source project
3+
*
4+
* Copyright (c) 2024 Apple Inc. and the Swift project authors
5+
* Licensed under Apache License v2.0 with Runtime Library Exception
6+
*
7+
* See https://swift.org/LICENSE.txt for license information
8+
* See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
import IndexStore from 'docc-render/stores/IndexStore';
11+
import Language from 'docc-render/constants/Language';
12+
13+
export default {
14+
computed: {
15+
indexNodes({ indexState: { flatChildren }, interfaceLanguage }) {
16+
if (!flatChildren) return [];
17+
return flatChildren[interfaceLanguage] ?? (flatChildren[Language.swift.key.url] || []);
18+
},
19+
technologyProps({ indexState: { technologyProps }, interfaceLanguage, technology }) {
20+
// Select technology props from fetched index data for the current language, fallback to swift
21+
// If none available, fallback to technology data of the curr page or null
22+
return technologyProps[interfaceLanguage] ?? technologyProps[Language.swift.key.url]
23+
?? (technology ? {
24+
technology: technology.title,
25+
technologyPath: technology.path || technology.url,
26+
isTechnologyBeta: technology.beta,
27+
} : null);
28+
},
29+
navigatorProps: ({
30+
indexNodes,
31+
indexState: {
32+
flatChildren,
33+
references,
34+
apiChanges,
35+
errorFetching,
36+
},
37+
technologyProps,
38+
}) => ({
39+
flatChildren: indexNodes,
40+
navigatorReferences: references,
41+
apiChanges,
42+
isFetching: !flatChildren && !errorFetching,
43+
errorFetching,
44+
technologyProps,
45+
}),
46+
},
47+
data() {
48+
return {
49+
indexState: IndexStore.state,
50+
};
51+
},
52+
};

src/stores/IndexStore.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010

1111
export default {
1212
state: {
13-
flatChildren: {},
13+
flatChildren: null,
1414
references: {},
1515
apiChanges: null,
1616
includedArchiveIdentifiers: [],
1717
errorFetching: false,
1818
technologyProps: {},
1919
},
2020
reset() {
21-
this.state.flatChildren = {};
21+
this.state.flatChildren = null;
2222
this.state.references = {};
2323
this.state.apiChanges = null;
2424
this.state.includedArchiveIdentifiers = [];

src/views/DocumentationTopic.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ import {
5454
import DocumentationTopic from 'theme/components/DocumentationTopic.vue';
5555
import DocumentationLayout from 'docc-render/components/DocumentationLayout.vue';
5656
import DocumentationTopicStore from 'docc-render/stores/DocumentationTopicStore';
57-
import indexProvider from 'theme/mixins/indexProvider';
57+
import indexDataFetcher from 'theme/mixins/indexDataFetcher';
5858
import Language from 'docc-render/constants/Language';
5959
import OnThisPageRegistrator from 'docc-render/mixins/onThisPageRegistrator';
6060
import { updateLocale } from 'theme/utils/i18n-utils';
@@ -75,7 +75,7 @@ export default {
7575
Topic: DocumentationTopic,
7676
DocumentationLayout,
7777
},
78-
mixins: [OnThisPageRegistrator, communicationBridgeUtils, indexProvider],
78+
mixins: [OnThisPageRegistrator, communicationBridgeUtils, indexDataFetcher],
7979
props: {
8080
enableMinimized: {
8181
type: Boolean,

tests/unit/mixins/__snapshots__/indexProvider.spec.js.snap renamed to tests/unit/mixins/__snapshots__/indexDataFetcher.spec.js.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`indexProvider counts the amount of deprecated items a groupMarker has 1`] = `
3+
exports[`indexDataFetcher counts the amount of deprecated items a groupMarker has 1`] = `
44
Object {
55
"swift": Array [
66
Object {
@@ -139,7 +139,7 @@ Object {
139139
}
140140
`;
141141
142-
exports[`indexProvider removes the \`beta\` flag from children, if the parent is a \`beta\` 1`] = `
142+
exports[`indexDataFetcher removes the \`beta\` flag from children, if the parent is a \`beta\` 1`] = `
143143
Object {
144144
"swift": Array [
145145
Object {
@@ -277,7 +277,7 @@ Object {
277277
}
278278
`;
279279
280-
exports[`indexProvider removes the \`beta\` flag from children, if the parent is a \`beta\` 2`] = `
280+
exports[`indexDataFetcher removes the \`beta\` flag from children, if the parent is a \`beta\` 2`] = `
281281
Object {
282282
"swift": Array [
283283
Object {

tests/unit/mixins/indexProvider.spec.js renamed to tests/unit/mixins/indexDataFetcher.spec.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* See https://swift.org/LICENSE.txt for license information
88
* See https://swift.org/CONTRIBUTORS.txt for Swift project authors
99
*/
10-
import indexProvider from 'docc-render/mixins/indexProvider';
10+
import indexDataFetcher from 'docc-render/mixins/indexDataFetcher';
1111
import IndexStore from 'docc-render/stores/IndexStore';
1212
import { shallowMount } from '@vue/test-utils';
1313
import { fetchData } from 'docc-render/utils/data';
@@ -173,12 +173,12 @@ fetchData.mockReturnValue(response);
173173
const Component = {
174174
name: 'MyComponent',
175175
template: '<div/>',
176-
mixins: [indexProvider],
176+
mixins: [indexDataFetcher],
177177
};
178178

179179
const createWrapper = ({ mocks } = {}) => shallowMount(Component, { mocks });
180180

181-
describe('indexProvider', () => {
181+
describe('indexDataFetcher', () => {
182182
beforeEach(() => {
183183
jest.clearAllMocks();
184184
});
@@ -187,7 +187,7 @@ describe('indexProvider', () => {
187187
IndexStore.reset();
188188
});
189189

190-
it('fetches data when parent component imports `indexProvider`', async () => {
190+
it('fetches data when parent component imports `indexDataFetcher`', async () => {
191191
expect(fetchData).toHaveBeenCalledTimes(0);
192192
const wrapper = createWrapper({
193193
mocks: {
@@ -230,7 +230,7 @@ describe('indexProvider', () => {
230230
expect(fetchData).toHaveBeenCalledTimes(1);
231231
await flushPromises();
232232
expect(IndexStore.state).toEqual({
233-
flatChildren: {},
233+
flatChildren: null,
234234
references: {},
235235
apiChanges: null,
236236
includedArchiveIdentifiers: [],
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/**
2+
* This source file is part of the Swift.org open source project
3+
*
4+
* Copyright (c) 2024 Apple Inc. and the Swift project authors
5+
* Licensed under Apache License v2.0 with Runtime Library Exception
6+
*
7+
* See https://swift.org/LICENSE.txt for license information
8+
* See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
import { shallowMount } from '@vue/test-utils';
11+
import Language from 'docc-render/constants/Language';
12+
import indexDataGetter from 'docc-render/mixins/indexDataGetter';
13+
import IndexStore from 'docc-render/stores/IndexStore';
14+
15+
const swiftIndex = [{
16+
id: 'foo',
17+
path: '/documentation/swift',
18+
children: [1, 2, 3],
19+
}];
20+
21+
const objectiveCIndex = [{
22+
id: 'foo-objc',
23+
path: '/documentation/objc',
24+
children: [1],
25+
}];
26+
27+
const references = {
28+
foo: { identifier: 'foo' },
29+
bar: { identifier: 'bar' },
30+
};
31+
32+
const apiChanges = {
33+
interfaceLanguages: {
34+
[Language.swift.key.url]: [],
35+
},
36+
};
37+
38+
const includedArchiveIdentifiers = ['foo', 'bar'];
39+
40+
const swiftProps = {
41+
technology: 'swift',
42+
technologyPath: '/documentation/swift',
43+
isTechnologyBeta: false,
44+
};
45+
46+
const objectiveCProps = {
47+
technology: 'objective-c',
48+
technologyPath: '/documentation/objc',
49+
isTechnologyBeta: false,
50+
};
51+
52+
const mockState = () => ({
53+
flatChildren: {
54+
[Language.swift.key.url]: swiftIndex,
55+
},
56+
references,
57+
apiChanges,
58+
includedArchiveIdentifiers,
59+
errorFetching: false,
60+
technologyProps: {
61+
[Language.swift.key.url]: swiftProps,
62+
},
63+
});
64+
65+
const Component = {
66+
name: 'MyComponent',
67+
template: '<div/>',
68+
mixins: [indexDataGetter],
69+
props: {
70+
interfaceLanguage: {
71+
type: String,
72+
required: false,
73+
},
74+
technology: {
75+
type: Object,
76+
required: false,
77+
},
78+
},
79+
};
80+
81+
const technology = {
82+
title: 'title',
83+
path: '/documentation/boo',
84+
};
85+
86+
const createWrapper = () => shallowMount(Component, {
87+
propsData: {
88+
interfaceLanguage: Language.swift.key.url,
89+
technology,
90+
},
91+
});
92+
93+
describe('indexDataGetter', () => {
94+
it('selects correct language variant when it exists`', async () => {
95+
IndexStore.state = mockState();
96+
const wrapper = createWrapper();
97+
expect(wrapper.vm.indexNodes).toEqual(swiftIndex);
98+
expect(wrapper.vm.technologyProps).toEqual(swiftProps);
99+
100+
wrapper.setData({
101+
indexState: {
102+
flatChildren: {
103+
[Language.objectiveC.key.url]: objectiveCIndex,
104+
},
105+
technologyProps: {
106+
[Language.objectiveC.key.url]: objectiveCProps,
107+
},
108+
},
109+
});
110+
wrapper.setProps({
111+
interfaceLanguage: Language.objectiveC.key.url,
112+
});
113+
expect(wrapper.vm.indexNodes).toEqual(objectiveCIndex);
114+
expect(wrapper.vm.technologyProps).toEqual(objectiveCProps);
115+
});
116+
117+
it('selects swift variant when language is objc but its data does not exist`', async () => {
118+
IndexStore.state = mockState();
119+
const wrapper = createWrapper();
120+
wrapper.setProps({
121+
interfaceLanguage: Language.objectiveC.key.url,
122+
});
123+
await wrapper.vm.$nextTick();
124+
expect(wrapper.vm.indexNodes).toEqual(swiftIndex);
125+
expect(wrapper.vm.technologyProps).toEqual(swiftProps);
126+
});
127+
128+
it('when no index data is unavailable through IndexStore, fallback to technology provided in prop`', () => {
129+
IndexStore.state = {
130+
...mockState(),
131+
flatChildren: {},
132+
technologyProps: {},
133+
};
134+
const wrapper = createWrapper();
135+
expect(wrapper.vm.indexNodes).toEqual([]);
136+
// fallback to technology if provided thru prop
137+
expect(wrapper.vm.technologyProps).toEqual({
138+
technology: technology.title,
139+
technologyPath: technology.path,
140+
isTechnologyBeta: technology.beta,
141+
});
142+
143+
wrapper.setProps({
144+
technology: null,
145+
});
146+
expect(wrapper.vm.indexNodes).toEqual([]);
147+
// if not provided, set to null
148+
expect(wrapper.vm.technologyProps).toEqual(null);
149+
});
150+
151+
it('computes the correct fetching status`', () => {
152+
IndexStore.state = {
153+
...mockState(),
154+
flatChildren: null,
155+
errorFetching: true,
156+
};
157+
const wrapper = createWrapper();
158+
// not `isFetching` if no fetched data and there was an error
159+
expect(wrapper.vm.navigatorProps.isFetching).toBe(false);
160+
161+
// fetching if no fetched data and no error
162+
wrapper.setData({
163+
indexState: {
164+
errorFetching: false,
165+
},
166+
});
167+
expect(wrapper.vm.navigatorProps.isFetching).toBe(true);
168+
169+
// not fetching if fetched data is not null
170+
wrapper.setData({
171+
indexState: {
172+
flatChildren: {},
173+
},
174+
});
175+
expect(wrapper.vm.navigatorProps.isFetching).toBe(false);
176+
});
177+
});

0 commit comments

Comments
 (0)