Skip to content

Commit bd4bcc7

Browse files
authored
Add sortLocale prop to components for enhanced sorting functionality (#146)
2 parents f1f2fad + c52d961 commit bd4bcc7

5 files changed

Lines changed: 152 additions & 18 deletions

File tree

app/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
RawResponseView,
99
TableView,
1010
TourStep,
11-
VizbuilderView,
11+
VizbuilderView
1212
} from "../src/main";
1313
import * as translations from "../translations";
1414
import {type ServerOption, SettingsProvider} from "./Settings";
@@ -147,6 +147,7 @@ function App() {
147147
{key: "vizbuilder", label: "Vizbuilder", component: VizbuilderView},
148148
{key: "raw", label: "Raw Response", component: RawResponseView}
149149
]}
150+
sortLocale="en"
150151
tourConfig={{
151152
introImage: (
152153
<Center p="xl" bg="dark.1">

src/components/Explorer.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ export function ExplorerComponent<Locale extends string>(props: {
9797
* @default "en"
9898
*/
9999
defaultLocale?: Locale;
100+
101+
/**
102+
* The locale to use for sorting cube items within the SelectCubes component.
103+
* This controls the sorting of subtopics and cube buttons.
104+
* If not set, the defaultLocale will be used.
105+
* @default undefined
106+
*/
107+
sortLocale?: string;
100108

101109
/**
102110
* Specifies which property should be used to filter elements in the member
@@ -197,6 +205,7 @@ export function ExplorerComponent<Locale extends string>(props: {
197205
}) {
198206
const {
199207
defaultLocale = "en",
208+
sortLocale,
200209
defaultOpenParams = "measures",
201210
height = "100vh",
202211
withinMantineProvider = true,
@@ -267,6 +276,7 @@ export function ExplorerComponent<Locale extends string>(props: {
267276
serverURL={props.serverURL}
268277
splash={props.splash}
269278
withMultiQuery={withMultiQuery}
279+
sortLocale={sortLocale}
270280
/>
271281
</ExplorerTour>
272282
</AppProviders>

src/components/ExplorerContent.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export function ExplorerContent(props: {
4545
serverURL: string;
4646
splash?: React.ComponentType<{translation: TranslationContextProps}>;
4747
withMultiQuery: boolean;
48+
sortLocale?: string;
4849
}) {
4950
const {classes} = useStyles({height: props.height});
5051
const {defaultLocale} = useSettings();
@@ -67,7 +68,7 @@ export function ExplorerContent(props: {
6768
<SideBarProvider locale={defaultLocale}>
6869
<SideBar>
6970
<SideBarItem>
70-
<SelectCubes locale={defaultLocale} />
71+
<SelectCubes locale={defaultLocale} sortLocale={props.sortLocale || defaultLocale} />
7172
</SideBarItem>
7273
</SideBar>
7374

src/components/Results.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type Props = {
1111
selectedItem?: TesseractCube;
1212
graph: Graph;
1313
locale: string;
14+
sortLocale: string;
1415
getCube: (
1516
items: AnnotatedCube[],
1617
table: string,
@@ -21,7 +22,7 @@ type Props = {
2122
};
2223

2324
export default function Results(props: Props) {
24-
const {graph, selectedItem, locale, getCube, isSelected} = props;
25+
const {graph, selectedItem, locale, sortLocale, getCube, isSelected} = props;
2526
const {classes} = useStyles();
2627
const {setExpanded, setInput, map} = useSideBar();
2728
const {onChangeCube} = useQueryItem();
@@ -32,7 +33,18 @@ export default function Results(props: Props) {
3233
const [topic, subtopic] = key.split(" - ");
3334

3435
// For each topic-subtopic, collect the cubes
35-
const topicResults = items
36+
const topicResults = [...items]
37+
.sort((a, b) => {
38+
// Get the localized cube names from the graph for sorting
39+
const cubeA = getCube(graph.items, a, subtopic, locale);
40+
const cubeB = getCube(graph.items, b, subtopic, locale);
41+
42+
// Use table annotation for cubes
43+
const aSort = cubeA ? getAnnotation(cubeA, "table", sortLocale) || a : a;
44+
const bSort = cubeB ? getAnnotation(cubeB, "table", sortLocale) || b : b;
45+
46+
return aSort.localeCompare(bSort, sortLocale, {sensitivity: "base"});
47+
})
3648
.map(item => {
3749
const cube = getCube(graph.items, item, subtopic, locale);
3850
if (!cube) return null;

src/components/SelectCubes.tsx

Lines changed: 124 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const loadingCubes = Array.from({length: 10}, (v, index) => ({
2323
id: `loading-cube-${index}`
2424
}));
2525

26-
export function SelectCubes({locale}: {locale: string}) {
26+
export function SelectCubes({locale, sortLocale}: {locale: string; sortLocale?: string}) {
2727
const items = useCubeItems();
2828
const selectedItem = useSelectedItem();
2929
const {schemaLoading} = useQueryItem();
@@ -36,19 +36,32 @@ export function SelectCubes({locale}: {locale: string}) {
3636
return null;
3737
}
3838

39-
return <SelectCubeInternal items={items} selectedItem={selectedItem} locale={locale} />;
39+
return (
40+
<SelectCubeInternal
41+
items={items}
42+
selectedItem={selectedItem}
43+
locale={locale}
44+
sortLocale={sortLocale || locale}
45+
/>
46+
);
4047
}
4148

4249
function SelectCubeInternal(props: {
4350
items: TesseractCube[];
4451
selectedItem: TesseractCube | undefined;
4552
locale: string;
53+
sortLocale: string;
4654
}) {
47-
const {items, selectedItem, locale} = props;
55+
const {items, selectedItem, locale, sortLocale} = props;
4856

4957
return (
5058
<Stack id="dex-select-cube" spacing={"xs"} w={"100%"}>
51-
<CubeTree items={items as AnnotatedCube[]} locale={locale} selectedItem={selectedItem} />
59+
<CubeTree
60+
items={items as AnnotatedCube[]}
61+
locale={locale}
62+
selectedItem={selectedItem}
63+
sortLocale={sortLocale}
64+
/>
5265
</Stack>
5366
);
5467
}
@@ -90,33 +103,46 @@ export function getKeys(
90103
}
91104

92105
function isSelected(selectedItem, currentItem) {
93-
if (selectedItem && currentItem) {
106+
if (selectedItem && currentItem && selectedItem.name && currentItem.name) {
94107
return selectedItem.name === currentItem.name;
95108
}
109+
return false;
96110
}
97111

98112
function getCube(items: AnnotatedCube[], name: string, subtopic: string, locale: string) {
113+
// Make sure items is an array before trying to find
114+
if (!Array.isArray(items)) return undefined;
115+
99116
const cube = items.find(
100-
item => item.name === name && getAnnotation(item, "subtopic", locale) === subtopic
117+
item => item && item.name === name && getAnnotation(item, "subtopic", locale) === subtopic
101118
);
102119
return cube;
103120
}
104121

105122
function CubeTree({
106123
locale,
107-
selectedItem
124+
selectedItem,
125+
sortLocale
108126
}: {
109127
items: AnnotatedCube[];
110128
locale: string;
111129
selectedItem?: TesseractCube;
130+
sortLocale: string;
112131
}) {
113132
const {map, input, graph} = useSideBar();
114133
const {translate: t} = useTranslation();
115134

135+
// We need both sets of topics - one for UI display (locale) and one for sorting (sortLocale)
116136
let topics = useMemo(
117137
() => getKeys(graph.items as AnnotatedCube[], "topic", locale),
118138
[graph.items, locale]
119139
);
140+
141+
// Get topics in the sort locale for proper sorting
142+
const sortTopics = useMemo(
143+
() => getKeys(graph.items as AnnotatedCube[], "topic", sortLocale),
144+
[graph.items, sortLocale]
145+
);
120146

121147
if (input.length > 0 && map && !(map.size > 0)) {
122148
// there is a query but not results in map
@@ -134,10 +160,18 @@ function CubeTree({
134160
isSelected={isSelected}
135161
graph={graph}
136162
locale={locale}
163+
sortLocale={sortLocale}
137164
/>
138165
) : (
139166
graph.items.length > 0 && (
140-
<RootAccordions items={topics} graph={graph} selectedItem={selectedItem} locale={locale} />
167+
<RootAccordions
168+
items={topics}
169+
sortItems={sortTopics}
170+
graph={graph}
171+
selectedItem={selectedItem}
172+
locale={locale}
173+
sortLocale={sortLocale}
174+
/>
141175
)
142176
);
143177
}
@@ -156,7 +190,7 @@ function useAccordionValue(key: Keys, locale) {
156190
return {value, setValue};
157191
}
158192

159-
function RootAccordions({items, graph, locale, selectedItem}) {
193+
function RootAccordions({items, sortItems, graph, locale, selectedItem, sortLocale}) {
160194
const {value, setValue} = useAccordionValue("topic", locale);
161195
return (
162196
<Accordion
@@ -190,16 +224,22 @@ function RootAccordions({items, graph, locale, selectedItem}) {
190224
{items
191225
.sort((a, b) => graph.topicOrder[a] - graph.topicOrder[b])
192226
.map(item => {
227+
// Find the corresponding item in sortLocale for sorting subtopics
228+
const sortItem = sortItems.find(topic =>
229+
graph.topicOrder[topic] === graph.topicOrder[item]
230+
) || item;
193231
return (
194232
<Accordion.Item value={`topic-${item}`} key={`topic-${item}`}>
195233
<AccordionControl>{item}</AccordionControl>
196234
<Accordion.Panel>
197235
<SubtopicAccordion
198236
graph={graph}
199237
parent={item}
238+
sortParent={sortItem}
200239
items={graph.adjList[item]}
201240
key={item}
202241
locale={locale}
242+
sortLocale={sortLocale}
203243
selectedItem={selectedItem}
204244
/>
205245
</Accordion.Panel>
@@ -215,18 +255,21 @@ function CubeButton({
215255
selectedItem,
216256
graph,
217257
locale,
258+
sortLocale,
218259
parent
219260
}: {
220261
item: string;
221262
selectedItem?: TesseractCube;
222263
graph: Graph;
223264
locale: string;
265+
sortLocale: string;
224266
parent?: string;
225267
}) {
226268
const {onChangeCube, membersLoading, schemaLoading} = useQueryItem();
227269
const {classes} = useLinkStyles();
228270
const isSelectionInProgress = membersLoading || schemaLoading;
229-
const table = graph.getName(item, locale);
271+
// Make sure we handle the case where getName might return undefined
272+
const table = graph.getName ? graph.getName(item, locale) : item;
230273
const subtopic = parent ?? "";
231274

232275
const handleClick = () => {
@@ -267,19 +310,23 @@ type NestedAccordionType = {
267310
items: string[];
268311
graph: any;
269312
parent?: string;
313+
sortParent?: string;
270314
selectedItem?: TesseractCube;
271315
locale: string;
316+
sortLocale: string;
272317
};
273318

274319
function SubtopicAccordion({
275320
items,
276321
graph,
277322
parent,
323+
sortParent,
278324
selectedItem,
279-
locale
325+
locale,
326+
sortLocale
280327
}: PropsWithChildren<NestedAccordionType>) {
281328
const {value, setValue} = useAccordionValue("subtopic", locale);
282-
329+
// Use sortParent for accessing the adjList when available, otherwise fall back to parent
283330
return (
284331
<Accordion
285332
id="data-accordion-topic"
@@ -306,9 +353,71 @@ function SubtopicAccordion({
306353
})}
307354
>
308355
{[...items]
309-
.sort((a, b) => a.localeCompare(b, locale, {sensitivity: "base"}))
356+
.sort((a, b) => {
357+
// Get the localized subtopic labels from the graph for sorting
358+
const aLabel = graph.items.find(cube =>
359+
getAnnotation(cube, "topic", locale) === parent &&
360+
getAnnotation(cube, "subtopic", locale) === a
361+
);
362+
363+
const bLabel = graph.items.find(cube =>
364+
getAnnotation(cube, "topic", locale) === parent &&
365+
getAnnotation(cube, "subtopic", locale) === b
366+
);
367+
368+
const aSort = aLabel ? getAnnotation(aLabel, "subtopic", sortLocale) || a : a;
369+
const bSort = bLabel ? getAnnotation(bLabel, "subtopic", sortLocale) || b : b;
370+
371+
return aSort.localeCompare(bSort, sortLocale, {sensitivity: "base"});
372+
})
310373
.map((item, index) => {
311-
const filtered = [...graph.adjList[item]].filter(value => value !== parent);
374+
// If we have a sortParent, we need to find the corresponding item in the sort locale
375+
// to properly access graph.adjList
376+
let sortSubtopic = item; // Default to the original item
377+
378+
if (sortParent && item) {
379+
// First check if we can find a cube with this subtopic in the current locale
380+
const hasCubeInLocale = graph.items
381+
.some(cube =>
382+
getAnnotation(cube, "topic", locale) === parent &&
383+
getAnnotation(cube, "subtopic", locale) === item
384+
);
385+
386+
if (hasCubeInLocale) {
387+
// Find the corresponding cube in the sort locale
388+
const matchingCube = graph.items
389+
.find(cube =>
390+
getAnnotation(cube, "topic", sortLocale) === sortParent &&
391+
getAnnotation(cube, "subtopic", locale) === item
392+
);
393+
394+
// Only get the annotation if we found a matching cube
395+
if (matchingCube) {
396+
const annotatedSubtopic = getAnnotation(matchingCube, "subtopic", sortLocale);
397+
if (annotatedSubtopic) {
398+
sortSubtopic = annotatedSubtopic;
399+
}
400+
}
401+
}
402+
}
403+
404+
// Make sure the item exists in the adjList to avoid errors
405+
const adjListItem = graph.adjList[sortSubtopic];
406+
const filtered = adjListItem ?
407+
[...adjListItem]
408+
.filter(value => value !== parent)
409+
.sort((a, b) => {
410+
// Get the localized cube names from the graph for sorting
411+
const aLabel = graph.items.find(cube => cube && cube.name === a);
412+
const bLabel = graph.items.find(cube => cube && cube.name === b);
413+
414+
// Use table annotation for cubes
415+
const aSort = aLabel ? getAnnotation(aLabel, "table", sortLocale) || a : a;
416+
const bSort = bLabel ? getAnnotation(bLabel, "table", sortLocale) || b : b;
417+
418+
return aSort.localeCompare(bSort, sortLocale, {sensitivity: "base"});
419+
})
420+
: [];
312421

313422
return (
314423
<Accordion.Item value={`subtopic-${item}`} key={`subtopic-${item}-${index}`}>
@@ -320,6 +429,7 @@ function SubtopicAccordion({
320429
graph={graph}
321430
item={table}
322431
locale={locale}
432+
sortLocale={sortLocale}
323433
selectedItem={selectedItem}
324434
parent={item}
325435
/>

0 commit comments

Comments
 (0)