Skip to content

Commit f00f0a0

Browse files
authored
docs(react-charts): add button to load more Plotly JSONs in DeclarativeChart (#35622)
1 parent 9dc5e0a commit f00f0a0

File tree

1 file changed

+79
-54
lines changed

1 file changed

+79
-54
lines changed

packages/charts/react-charts/stories/src/DeclarativeChart/DeclarativeChartDefault.stories.tsx

Lines changed: 79 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as React from 'react';
22
import type { JSXElement } from '@fluentui/react-components';
33
import { DeclarativeChart, IDeclarativeChart, Schema } from '@fluentui/react-charts';
44
import {
5+
Button,
56
Dropdown,
67
Field,
78
Input,
@@ -158,18 +159,7 @@ const DEFAULT_SCHEMAS = [
158159
const dropdownStyles = { width: 200 };
159160
const inputStyles = { maxWidth: 300 };
160161

161-
const cachedFetch = (url: string) => {
162-
const cachedData = localStorage.getItem(url);
163-
if (cachedData) {
164-
return Promise.resolve(JSON.parse(cachedData));
165-
}
166-
return fetch(url)
167-
.then(response => response.json())
168-
.then(data => {
169-
localStorage.setItem(url, JSON.stringify(data));
170-
return data;
171-
});
172-
};
162+
type LoadingState = 'initial' | 'loading' | 'partially_loaded' | 'loaded';
173163

174164
export const DeclarativeChartBasicExample = (): JSXElement => {
175165
const declarativeChartRef = React.useRef<IDeclarativeChart>(null);
@@ -185,37 +175,42 @@ export const DeclarativeChartBasicExample = (): JSXElement => {
185175
const [fluentDataVizColorPalette, setFluentDataVizColorPalette] =
186176
React.useState<FluentDataVizColorPaletteTypes>('default');
187177
const [showMore, setShowMore] = React.useState(false);
188-
const [isLoading, setLoading] = React.useState(false);
178+
const [loadingState, setLoadingState] = React.useState<LoadingState>('initial');
189179

190180
React.useEffect(() => {
191181
doc?.addEventListener('contextmenu', e => {
192182
e.preventDefault();
193183
});
194184
}, [doc]);
195185

196-
React.useEffect(() => {
197-
const loadSchemas = async () => {
198-
setLoading(true);
199-
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
200-
const _schemas: { key: string; schema: any }[] = [];
201-
for (let i = 1; i <= 80; i++) {
202-
try {
203-
const filename = `data_${('00' + i).slice(-3)}`;
204-
const schema = await cachedFetch(
205-
`https://raw.githubusercontent.com/microsoft/fluentui-charting-contrib/refs/heads/main/apps/plotly_examples/src/data/${filename}.json`,
206-
);
207-
_schemas.push({ key: filename, schema });
208-
} catch (error) {
209-
// Nothing to do here
210-
}
211-
}
212-
loadedSchemas.current = _schemas;
213-
setLoading(false);
214-
};
215-
216-
loadSchemas();
186+
const loadSchemas = React.useCallback(async (startLoadingState: LoadingState = 'loading') => {
187+
setLoadingState(startLoadingState);
188+
let disableLoadMore = false;
189+
const offset = loadedSchemas.current.length;
190+
const promises = Array.from({ length: 100 }, (_, index) => {
191+
const id = offset + index + 1;
192+
const filename = `data_${id < 100 ? ('00' + id).slice(-3) : id}`;
193+
return fetch(
194+
`https://raw.githubusercontent.com/microsoft/fluentui-charting-contrib/refs/heads/main/apps/plotly_examples/src/data/${filename}.json`,
195+
)
196+
.then(response => {
197+
if (response.status === 404) {
198+
disableLoadMore = true;
199+
return null;
200+
}
201+
return response.json();
202+
})
203+
.then(schema => ({ key: filename, schema }))
204+
.catch(() => ({ key: filename, schema: {} }));
205+
});
206+
loadedSchemas.current.push(...(await Promise.all(promises)).filter(item => item.schema !== null));
207+
setLoadingState(disableLoadMore ? 'loaded' : 'partially_loaded');
217208
}, []);
218209

210+
React.useEffect(() => {
211+
loadSchemas('initial');
212+
}, [loadSchemas]);
213+
219214
const getSchemaByKey = React.useCallback(
220215
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
221216
(key: string): any => {
@@ -225,38 +220,57 @@ export const DeclarativeChartBasicExample = (): JSXElement => {
225220
[showMore],
226221
);
227222

223+
const applySelection = React.useCallback(
224+
(option: { key: string; text: string } | undefined) => {
225+
if (!option) {
226+
return;
227+
}
228+
setSelectedOptions([option.key]);
229+
setDropdownValue(option.text);
230+
const schema = getSchemaByKey(option.key);
231+
setSelectedLegends(schema.selectedLegends ? JSON.stringify(schema.selectedLegends) : '');
232+
},
233+
[getSchemaByKey],
234+
);
235+
228236
React.useEffect(() => {
229-
if (showMore && (isLoading || loadedSchemas.current.length === 0)) {
237+
if (!showMore) {
238+
setOptions(DEFAULT_OPTIONS);
239+
applySelection(DEFAULT_OPTIONS[0]);
240+
return;
241+
}
242+
// showMore === true
243+
if (loadingState === 'initial' || loadedSchemas.current.length === 0) {
230244
setOptions([]);
231245
setSelectedOptions([]);
232246
setDropdownValue('');
233247
setSelectedLegends('');
234-
} else {
235-
const _options = showMore
236-
? loadedSchemas.current.map(schema => ({ key: schema.key, text: schema.key }))
237-
: DEFAULT_OPTIONS;
248+
return;
249+
}
250+
const _options = loadedSchemas.current.map(schema => ({ key: schema.key, text: schema.key }));
251+
if (loadingState.includes('loaded')) {
238252
setOptions(_options);
239-
setSelectedOptions([_options[0].key]);
240-
setDropdownValue(_options[0].text);
241-
const selectedPlotlySchema = getSchemaByKey(_options[0].key);
242-
const { selectedLegends: _selectedLegends } = selectedPlotlySchema;
243-
setSelectedLegends(_selectedLegends ? JSON.stringify(_selectedLegends) : '');
244253
}
245-
}, [showMore, isLoading, getSchemaByKey]);
254+
setSelectedOptions(prevSelectedOptions => {
255+
if (prevSelectedOptions[0]?.includes('data_')) {
256+
return prevSelectedOptions;
257+
}
258+
setDropdownValue(_options[0].text);
259+
const schema = getSchemaByKey(_options[0].key);
260+
setSelectedLegends(schema.selectedLegends ? JSON.stringify(schema.selectedLegends) : '');
261+
return [_options[0].key];
262+
});
263+
}, [showMore, loadingState, applySelection, getSchemaByKey]);
246264

247265
const onSwitchDataChange = React.useCallback((ev: React.ChangeEvent<HTMLInputElement>) => {
248266
setShowMore(ev.currentTarget.checked);
249267
}, []);
250268

251269
const onOptionSelect = React.useCallback(
252270
(ev: SelectionEvents, data: OptionOnSelectData) => {
253-
setSelectedOptions(data.selectedOptions);
254-
setDropdownValue(data.optionText ?? '');
255-
const selectedPlotlySchema = getSchemaByKey(data.selectedOptions[0]);
256-
const { selectedLegends: _selectedLegends } = selectedPlotlySchema;
257-
setSelectedLegends(_selectedLegends ? JSON.stringify(_selectedLegends) : '');
271+
applySelection({ key: data.selectedOptions[0], text: data.optionText ?? '' });
258272
},
259-
[getSchemaByKey],
273+
[applySelection],
260274
);
261275

262276
const onSelectedLegendsChange = React.useCallback(
@@ -293,7 +307,7 @@ export const DeclarativeChartBasicExample = (): JSXElement => {
293307

294308
const renderDeclarativeChart = React.useCallback(() => {
295309
if (showMore) {
296-
if (isLoading) {
310+
if (loadingState === 'initial') {
297311
return <Spinner label="Loading..." />;
298312
} else if (loadedSchemas.current.length === 0) {
299313
return <div>More examples could not be loaded.</div>;
@@ -325,7 +339,7 @@ export const DeclarativeChartBasicExample = (): JSXElement => {
325339
);
326340
}, [
327341
showMore,
328-
isLoading,
342+
loadingState,
329343
selectedOptions,
330344
selectedLegends,
331345
getSchemaByKey,
@@ -336,7 +350,7 @@ export const DeclarativeChartBasicExample = (): JSXElement => {
336350
return (
337351
<div>
338352
<Switch checked={showMore} onChange={onSwitchDataChange} label={showMore ? 'Show more' : 'Show few'} />
339-
<div style={{ display: 'flex', flexDirection: 'row', gap: '10px' }}>
353+
<div style={{ display: 'flex', flexDirection: 'row', gap: '10px', alignItems: 'end' }}>
340354
<Field label="Select a schema">
341355
<Dropdown
342356
value={dropdownValue}
@@ -351,6 +365,17 @@ export const DeclarativeChartBasicExample = (): JSXElement => {
351365
))}
352366
</Dropdown>
353367
</Field>
368+
{showMore && (
369+
<div>
370+
<Button
371+
icon={loadingState.includes('loaded') ? undefined : <Spinner size="tiny" />}
372+
onClick={() => loadSchemas()}
373+
disabledFocusable={loadingState !== 'partially_loaded'}
374+
>
375+
{loadingState.includes('loaded') ? 'Load more' : 'Loading'}
376+
</Button>
377+
</div>
378+
)}
354379
<Field label="Select a color palette">
355380
<Dropdown
356381
value={fluentDataVizColorPalette}

0 commit comments

Comments
 (0)