diff --git a/packages/react-components/src/components/resource-explorers/explorers/asset-explorer/internal-asset-explorer.tsx b/packages/react-components/src/components/resource-explorers/explorers/asset-explorer/internal-asset-explorer.tsx
index d4a998c26..c8f2eeb30 100644
--- a/packages/react-components/src/components/resource-explorers/explorers/asset-explorer/internal-asset-explorer.tsx
+++ b/packages/react-components/src/components/resource-explorers/explorers/asset-explorer/internal-asset-explorer.tsx
@@ -57,7 +57,7 @@ export function InternalAssetExplorer({
const tableResourceDefinition =
customTableResourceDefinition ??
createDefaultAssetTableDefinition((asset) => {
- if ((asset.hierarchies ?? []).length > 0) {
+ if ((asset.hierarchies ?? []).length > 0 && parameters === undefined) {
return (
onClickAssetName(asset)}>{asset.name}
);
diff --git a/packages/react-components/src/components/resource-explorers/explorers/asset-explorer/use-assets/use-assets.ts b/packages/react-components/src/components/resource-explorers/explorers/asset-explorer/use-assets/use-assets.ts
index 2d2b450ec..abf3b27fe 100644
--- a/packages/react-components/src/components/resource-explorers/explorers/asset-explorer/use-assets/use-assets.ts
+++ b/packages/react-components/src/components/resource-explorers/explorers/asset-explorer/use-assets/use-assets.ts
@@ -53,7 +53,9 @@ export function useAssets({
const shouldRequestAssetModelAssets =
!shouldRequestChildAssets && isEveryAssetModelAssetsParameters(parameters);
const shouldRequestRootAssets =
- !shouldRequestAssetModelAssets && !shouldRequestChildAssets;
+ !shouldRequestAssetModelAssets &&
+ !shouldRequestChildAssets &&
+ parameters === undefined;
const assetSearchResult = useAssetSearch({
parameters: shouldRequestSearchedAssets ? parameters : [],
@@ -85,7 +87,15 @@ export function useAssets({
? assetModelAssetsQueryResult
: shouldRequestChildAssets
? childAssetsQueryResult
- : rootAssetsQueryResult;
+ : shouldRequestRootAssets
+ ? rootAssetsQueryResult
+ : {
+ assets: [],
+ isLoading: false,
+ error: undefined,
+ hasNextPage: false,
+ nextPage: () => {},
+ };
return queryResult;
}
diff --git a/packages/react-components/src/components/resource-explorers/explorers/time-series-explorer/internal-time-series-explorer.tsx b/packages/react-components/src/components/resource-explorers/explorers/time-series-explorer/internal-time-series-explorer.tsx
index 33ceac2a1..2c8630240 100644
--- a/packages/react-components/src/components/resource-explorers/explorers/time-series-explorer/internal-time-series-explorer.tsx
+++ b/packages/react-components/src/components/resource-explorers/explorers/time-series-explorer/internal-time-series-explorer.tsx
@@ -32,7 +32,7 @@ import { DEFAULT_TIME_SERIES_DROP_DOWN_DEFINITION } from '../../constants/drop-d
export function InternalTimeSeriesExplorer({
requestFns,
- parameters = [],
+ parameters = [{}],
resourceName = DEFAULT_TIME_SERIES_RESOURCE_NAME,
pluralResourceName = DEFAULT_PLURAL_TIME_SERIES_RESOURCE_NAME,
isTimeSeriesDisabled = DEFAULT_IS_RESOURCE_DISABLED,
diff --git a/packages/react-components/src/components/resource-explorers/requests/use-multiple-list-requests/types.ts b/packages/react-components/src/components/resource-explorers/requests/use-multiple-list-requests/types.ts
new file mode 100644
index 000000000..c291b050c
--- /dev/null
+++ b/packages/react-components/src/components/resource-explorers/requests/use-multiple-list-requests/types.ts
@@ -0,0 +1,9 @@
+type Parameters = readonly unknown[];
+
+export type QueryKey = readonly [
+ {
+ resourceId: string;
+ allParameters: Parameters;
+ currentParameters: Parameters[number];
+ }
+];
diff --git a/packages/react-components/src/components/resource-explorers/requests/use-multiple-list-requests/use-cached-resources.ts b/packages/react-components/src/components/resource-explorers/requests/use-multiple-list-requests/use-cached-resources.ts
index cd5e1a66b..108fd022f 100644
--- a/packages/react-components/src/components/resource-explorers/requests/use-multiple-list-requests/use-cached-resources.ts
+++ b/packages/react-components/src/components/resource-explorers/requests/use-multiple-list-requests/use-cached-resources.ts
@@ -1,7 +1,10 @@
import { useQueryClient } from '@tanstack/react-query';
+
+import type { QueryKey } from './types';
import { resourceExplorerQueryClient } from '../resource-explorer-query-client';
export interface UseCachedResourcesOptions {
+ resourceId: string;
allParameters: readonly unknown[];
}
@@ -9,13 +12,22 @@ export type UseCachedResourcesResult = Resource[];
/** Use resources loaded into the cache. */
export function useCachedResources({
+ resourceId,
allParameters,
}: UseCachedResourcesOptions): UseCachedResourcesResult {
const queryClient = useQueryClient(resourceExplorerQueryClient);
const resourcePages = queryClient.getQueriesData({
- queryKey: [{ allParameters }],
+ queryKey: [{ resourceId, allParameters }],
+ // Further filter the data to prevent partial matching on allParameters
+ predicate: (query) => {
+ return (
+ JSON.stringify((query.queryKey as QueryKey)[0].allParameters) ===
+ JSON.stringify(allParameters)
+ );
+ },
});
+
const resources = resourcePages.flatMap(([_, resources = []]) => resources);
return resources;
diff --git a/packages/react-components/src/components/resource-explorers/requests/use-multiple-list-requests/use-multiple-list-requests.ts b/packages/react-components/src/components/resource-explorers/requests/use-multiple-list-requests/use-multiple-list-requests.ts
index 81441d8f0..80703f2e2 100644
--- a/packages/react-components/src/components/resource-explorers/requests/use-multiple-list-requests/use-multiple-list-requests.ts
+++ b/packages/react-components/src/components/resource-explorers/requests/use-multiple-list-requests/use-multiple-list-requests.ts
@@ -2,6 +2,7 @@ import { useQuery } from '@tanstack/react-query';
import { useQueryPagination } from './use-two-dimensional-pagination';
import { useCachedResources } from './use-cached-resources';
+import type { QueryKey } from './types';
import {
ListRequestBaseParams,
ListRequestBaseResponse,
@@ -24,16 +25,6 @@ export interface UseMultipleListRequestsResult
resources: Resource[];
}
-type Parameters = readonly unknown[];
-
-type QueryKey = [
- {
- resourceId: string;
- allParameters: Parameters;
- currentParameters: Parameters[number];
- }
-];
-
/** Use paginated resources across multiple queries. */
export function useMultipleListRequests<
Params extends ListRequestBaseParams,
@@ -99,6 +90,7 @@ export function useMultipleListRequests<
);
const resources = useCachedResources({
+ resourceId,
allParameters: parameters,
});
diff --git a/packages/react-components/src/components/resource-explorers/testing/helpers/selectors.ts b/packages/react-components/src/components/resource-explorers/testing/helpers/selectors.ts
deleted file mode 100644
index c8911ea58..000000000
--- a/packages/react-components/src/components/resource-explorers/testing/helpers/selectors.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { screen } from '@testing-library/react';
-
-export function createSelectLoadingResources(pluralResourceType: string) {
- return function selectLoadingResources() {
- return screen.queryByText(`Loading ${pluralResourceType.toLowerCase()}...`);
- };
-}
-
-export function selectTableSearch() {
- return screen.queryByLabelText('Search');
-}
-
-export function selectTableFilter() {
- return screen.queryByLabelText('Filter');
-}
-
-export function selectPreviousPageButton() {
- return screen.getByRole('button', {
- name: 'Previous page',
- });
-}
-
-export function selectNextPageButton() {
- return screen.getByRole('button', { name: 'Next page' });
-}
diff --git a/packages/react-components/src/components/resource-explorers/testing/helpers/table.tsx b/packages/react-components/src/components/resource-explorers/testing/helpers/table.tsx
index 9b3928f00..d57387fc5 100644
--- a/packages/react-components/src/components/resource-explorers/testing/helpers/table.tsx
+++ b/packages/react-components/src/components/resource-explorers/testing/helpers/table.tsx
@@ -62,6 +62,18 @@ export function queryColumnDisplayCheckbox(columnName: string) {
return screen.queryByRole('checkbox', { name: columnName });
}
+export function getSearchField() {
+ return screen.getByLabelText('Search');
+}
+
+export function querySearchField() {
+ return screen.queryByLabelText('Search');
+}
+
+export async function typeSearchStatement(searchStatement: string) {
+ await userEvent.type(getSearchField(), searchStatement);
+}
+
export async function clickSearch() {
await waitFor(() => {
userEvent.click(screen.getByRole('button', { name: 'Search' }));
@@ -72,3 +84,14 @@ export async function clickSearch() {
expect(screen.queryByText(/Loading/)).not.toBeInTheDocument();
});
}
+
+export async function pressReturnKeyToSearch() {
+ await waitFor(() => {
+ userEvent.keyboard('{Enter}');
+ expect(screen.getByText(/Loading/)).toBeVisible();
+ });
+
+ await waitFor(() => {
+ expect(screen.queryByText(/Loading/)).not.toBeInTheDocument();
+ });
+}
diff --git a/packages/react-components/src/components/resource-explorers/testing/table-variant/asset-model-table.spec.tsx b/packages/react-components/src/components/resource-explorers/testing/table-variant/asset-model-table.spec.tsx
index b49b9c278..bf1b90b15 100644
--- a/packages/react-components/src/components/resource-explorers/testing/table-variant/asset-model-table.spec.tsx
+++ b/packages/react-components/src/components/resource-explorers/testing/table-variant/asset-model-table.spec.tsx
@@ -47,7 +47,7 @@ describe('asset model table', () => {
expect(screen.getByText('(0)')).toBeVisible();
// Search
- expect(screen.queryByLabelText('Search')).not.toBeInTheDocument();
+ expect(table.querySearchField()).not.toBeInTheDocument();
// Filter
expect(screen.queryByLabelText('Filter')).not.toBeInTheDocument();
diff --git a/packages/react-components/src/components/resource-explorers/testing/table-variant/asset-property-table.spec.tsx b/packages/react-components/src/components/resource-explorers/testing/table-variant/asset-property-table.spec.tsx
index dc7d633f9..3aa9bca9b 100644
--- a/packages/react-components/src/components/resource-explorers/testing/table-variant/asset-property-table.spec.tsx
+++ b/packages/react-components/src/components/resource-explorers/testing/table-variant/asset-property-table.spec.tsx
@@ -59,7 +59,7 @@ describe('asset property table', () => {
expect(screen.getByText('(0)')).toBeVisible();
// Search
- expect(screen.queryByLabelText('Search')).not.toBeInTheDocument();
+ expect(table.querySearchField()).not.toBeInTheDocument();
// Filter
expect(screen.queryByLabelText('Filter')).not.toBeInTheDocument();
@@ -90,7 +90,7 @@ describe('asset property table', () => {
);
- expect(screen.getByLabelText('Search')).toBeVisible();
+ expect(table.getSearchField()).toBeVisible();
});
it('renders with filter enabled', () => {
@@ -364,14 +364,13 @@ describe('asset property table', () => {
.mockResolvedValue({ rows: [] } satisfies Awaited<
ReturnType
>);
- const user = userEvent.setup();
render(
);
- await user.type(screen.getByLabelText('Search'), 'Asset Property');
+ await table.typeSearchStatement('Asset Property');
await table.clickSearch();
const queryStatement = executeQuery.mock.calls[0][0].queryStatement;
@@ -415,7 +414,6 @@ describe('asset property table', () => {
const executeQuery = jest.fn().mockResolvedValue({
rows: [assetPropertyRow1, assetPropertyRow2, assetPropertyRow3],
} satisfies Awaited>);
- const user = userEvent.setup();
render(
{
/>
);
- await user.type(screen.getByLabelText('Search'), 'Asset Property');
+ await table.typeSearchStatement('Asset Property');
await table.clickSearch();
// Name is rendered
@@ -548,6 +546,24 @@ describe('asset property table', () => {
screen.queryByText(assetPropertyRow3.data[3].scalarValue)
).not.toBeInTheDocument();
});
+
+ it('initiates search when user presses enter/return key', async () => {
+ const executeQuery = jest
+ .fn()
+ .mockResolvedValue({ rows: [] } satisfies Awaited<
+ ReturnType
+ >);
+ render(
+
+ );
+ await table.typeSearchStatement('Asset Property');
+ await table.pressReturnKeyToSearch();
+
+ expect(executeQuery).toHaveBeenCalledOnce();
+ });
});
describe('latest values', () => {
diff --git a/packages/react-components/src/components/resource-explorers/testing/table-variant/asset-table.spec.tsx b/packages/react-components/src/components/resource-explorers/testing/table-variant/asset-table.spec.tsx
index aca70e188..24ac1a308 100644
--- a/packages/react-components/src/components/resource-explorers/testing/table-variant/asset-table.spec.tsx
+++ b/packages/react-components/src/components/resource-explorers/testing/table-variant/asset-table.spec.tsx
@@ -45,7 +45,7 @@ describe('asset table', () => {
expect(screen.getByText('(0)')).toBeVisible();
// Search
- expect(screen.queryByLabelText('Search')).not.toBeInTheDocument();
+ expect(table.querySearchField()).not.toBeInTheDocument();
// Filter
expect(screen.queryByLabelText('Filter')).not.toBeInTheDocument();
@@ -72,7 +72,7 @@ describe('asset table', () => {
it('renders with search enabled', () => {
render();
- expect(screen.getByLabelText('Search')).toBeVisible();
+ expect(table.getSearchField()).toBeVisible();
});
it('renders with filter enabled', () => {
@@ -249,14 +249,13 @@ describe('asset table', () => {
.mockResolvedValue({ rows: [] } satisfies Awaited<
ReturnType
>);
- const user = userEvent.setup();
render(
);
- await user.type(screen.getByLabelText('Search'), 'Asset');
+ await table.typeSearchStatement('Asset');
await table.clickSearch();
const queryStatement = executeQuery.mock.calls[0][0].queryStatement;
@@ -299,7 +298,6 @@ describe('asset table', () => {
const executeQuery = jest.fn().mockResolvedValue({
rows: [assetRow1, assetRow2, assetRow3],
} satisfies Awaited>);
- const user = userEvent.setup();
render(
{
/>
);
- await user.type(screen.getByLabelText('Search'), 'Asset');
+ await table.typeSearchStatement('Asset');
await table.clickSearch();
// Name is rendered
@@ -408,6 +406,24 @@ describe('asset table', () => {
screen.queryByText(assetRow3.data[3].scalarValue)
).not.toBeInTheDocument();
});
+
+ it('initiates search when user presses enter/return key', async () => {
+ const executeQuery = jest
+ .fn()
+ .mockResolvedValue({ rows: [] } satisfies Awaited<
+ ReturnType
+ >);
+ render(
+
+ );
+ await table.typeSearchStatement('Asset');
+ await table.pressReturnKeyToSearch();
+
+ expect(executeQuery).toHaveBeenCalledOnce();
+ });
});
describe('selection', () => {
diff --git a/packages/react-components/src/components/resource-explorers/testing/table-variant/time-series-table.spec.tsx b/packages/react-components/src/components/resource-explorers/testing/table-variant/time-series-table.spec.tsx
index d1742014a..cf54655d3 100644
--- a/packages/react-components/src/components/resource-explorers/testing/table-variant/time-series-table.spec.tsx
+++ b/packages/react-components/src/components/resource-explorers/testing/table-variant/time-series-table.spec.tsx
@@ -52,7 +52,7 @@ describe('time series table', () => {
expect(screen.getByText('(0)')).toBeVisible();
// Search
- expect(screen.queryByLabelText('Search')).not.toBeInTheDocument();
+ expect(table.querySearchField()).not.toBeInTheDocument();
// Filter
expect(screen.queryByLabelText('Filter')).not.toBeInTheDocument();
diff --git a/packages/react-components/src/components/resource-explorers/types/resource-explorer.ts b/packages/react-components/src/components/resource-explorers/types/resource-explorer.ts
index 072c8d7dd..cc6828783 100644
--- a/packages/react-components/src/components/resource-explorers/types/resource-explorer.ts
+++ b/packages/react-components/src/components/resource-explorers/types/resource-explorer.ts
@@ -17,7 +17,7 @@ import {
} from './drop-down';
/** Props common to all resource explorers. */
-export type CommonResourceExplorerProps = {
+export type CommonResourceExplorerProps = {
/** TODO */
requestFns?: unknown;
diff --git a/packages/react-components/src/components/resource-explorers/variants/resource-table/resource-table-search.tsx b/packages/react-components/src/components/resource-explorers/variants/resource-table/resource-table-search.tsx
index 41404370c..eb0d36c9e 100644
--- a/packages/react-components/src/components/resource-explorers/variants/resource-table/resource-table-search.tsx
+++ b/packages/react-components/src/components/resource-explorers/variants/resource-table/resource-table-search.tsx
@@ -20,6 +20,12 @@ export function ResourceTableSearch({
}: ResourceTableSearchProps) {
const [searchInputValue, setSearchInputValue] = useState('');
+ function handleKeyDown(key: string) {
+ if (key === 'Enter') {
+ onClickSearch(searchInputValue);
+ }
+ }
+
function handleClickSearch() {
onClickSearch(searchInputValue);
}
@@ -49,6 +55,7 @@ export function ResourceTableSearch({
onChange={({ detail: { value } }) => setSearchInputValue(value)}
placeholder='Search for resources'
controlId='search'
+ onKeyDown={({ detail: { key } }) => handleKeyDown(key)}
/>
diff --git a/packages/react-components/stories/resource-explorers/asset-explorer.stories.tsx b/packages/react-components/stories/resource-explorers/asset-explorer.stories.tsx
index 070e9a7bf..2b2450c8a 100644
--- a/packages/react-components/stories/resource-explorers/asset-explorer.stories.tsx
+++ b/packages/react-components/stories/resource-explorers/asset-explorer.stories.tsx
@@ -1,107 +1,101 @@
import { type Meta } from '@storybook/react';
-import React, { useState } from 'react';
+import React from 'react';
-import { SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES } from './constants';
+import {
+ CommonResourceExplorerControls,
+ SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES,
+} from './controls';
import { client } from './data-source';
import {
AssetExplorer,
type AssetExplorerProps,
} from '../../src/components/resource-explorers';
-import type { ResourceExplorerStoryControls } from './types';
-import type { AssetResource } from '../../src/components/resource-explorers/types/resources';
+import {
+ StoryWithClearedResourceCache,
+ StoryWithSelectableResource,
+ StoryWithTanstackDevTools,
+} from './decorators';
+import { StoryFnReactReturnType } from '@storybook/react/dist/ts3.9/client/preview/types';
export default {
title: 'Resource Explorers/Asset Explorer',
component: AssetExplorer,
+ parameters: {
+ controls: {
+ expanded: true,
+ },
+ },
+ decorators: [
+ StoryWithTanstackDevTools,
+ StoryWithClearedResourceCache,
+ StoryWithSelectableResource,
+ ],
argTypes: {
...SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES,
},
} satisfies Meta;
-type AssetExplorerStoryControls = ResourceExplorerStoryControls;
+type AssetExplorerStory = (
+ controls: AssetExplorerStoryControls,
+ context: AssetExplorerStoryContext
+) => StoryFnReactReturnType;
-export function StandardExample({
- isTitleEnabled,
- isSearchEnabled,
- isFilterEnabled,
- isUserSettingsEnabled,
- ...assetExplorerProps
-}: AssetExplorerStoryControls) {
- const [selectedAssets, setSelectedAssets] = useState<
- NonNullable
- >([]);
+type AssetExplorerStoryControls = CommonResourceExplorerControls;
- return (
-
- );
+interface AssetExplorerStoryContext {
+ selectedResources: NonNullable;
+ onSelectResource: NonNullable;
}
-export function SearchOnly() {
- return (
-
- );
+function storyArgsToProps(
+ {
+ isTableTitleEnabled,
+ isTableSearchEnabled,
+ isTableFilterEnabled,
+ isTableUserSettingsEnabled,
+ isDropDownFilterEnabled,
+ ...controls
+ }: AssetExplorerStoryControls,
+ { selectedResources, onSelectResource }: AssetExplorerStoryContext
+): AssetExplorerProps {
+ return {
+ selectedAssets: selectedResources,
+ onSelectAsset: onSelectResource,
+ tableSettings: {
+ isTitleEnabled: isTableTitleEnabled,
+ isSearchEnabled: isTableSearchEnabled,
+ isFilterEnabled: isTableFilterEnabled,
+ isUserSettingsEnabled: isTableUserSettingsEnabled,
+ },
+ dropDownSettings: {
+ isFilterEnabled: isDropDownFilterEnabled,
+ },
+ ...controls,
+ };
}
-export function ZeroConfiguration() {
- return ;
-}
+export const HierarchyNavigation: AssetExplorerStory = (controls, context) => {
+ const props = storyArgsToProps(controls, context);
-export function ZeroConfigurationDropDown() {
- return ;
-}
+ return ;
+};
-export function GrafanaThemeTable() {
- const [selectedAssets, setSelectedAssets] = useState<
- NonNullable
- >([]);
+export const SearchOnly: AssetExplorerStory = (controls, context) => {
+ const props = storyArgsToProps(controls, context);
return (
);
-}
+};
-export function GrafanaThemeDropDown() {
- const [selectedAssets, setSelectedAssets] = useState<
- NonNullable
- >([]);
+export const ZeroConfigurationTable: AssetExplorerStory = () => {
+ return ;
+};
- return (
-
- );
-}
+export const ZeroConfigurationDropDown: AssetExplorerStory = () => {
+ return ;
+};
diff --git a/packages/react-components/stories/resource-explorers/asset-model-explorer.stories.tsx b/packages/react-components/stories/resource-explorers/asset-model-explorer.stories.tsx
index 855235b77..b2e99aac7 100644
--- a/packages/react-components/stories/resource-explorers/asset-model-explorer.stories.tsx
+++ b/packages/react-components/stories/resource-explorers/asset-model-explorer.stories.tsx
@@ -1,76 +1,123 @@
import { type Meta } from '@storybook/react';
-import React, { useState } from 'react';
+import React from 'react';
-import { SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES } from './constants';
+import {
+ SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES,
+ type CommonResourceExplorerControls,
+} from './controls';
import { client } from './data-source';
-import type { ResourceExplorerStoryControls } from './types';
-import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import {
AssetModelExplorer,
type AssetModelExplorerProps,
} from '../../src/components/resource-explorers';
-import type { AssetModelResource } from '../../src/components/resource-explorers/types/resources';
-import { resourceExplorerQueryClient } from '../../src/components/resource-explorers/requests/resource-explorer-query-client';
+import {
+ StoryWithClearedResourceCache,
+ StoryWithSelectableResource,
+ StoryWithTanstackDevTools,
+} from './decorators';
+import { StoryFnReactReturnType } from '@storybook/react/dist/ts3.9/client/preview/types';
export default {
title: 'Resource Explorers/Asset Model Explorer',
component: AssetModelExplorer,
+ parameters: {
+ controls: {
+ expanded: true,
+ exclude: ['tableSettings.isSearchEnabled'],
+ },
+ },
+ decorators: [
+ StoryWithTanstackDevTools,
+ StoryWithClearedResourceCache,
+ StoryWithSelectableResource,
+ ],
argTypes: {
...SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES,
- assetModelTypes: {
- type: 'radio',
- default: undefined,
- options: [undefined, 'ASSET_MODEL', 'COMPONENT_MODEL'],
- mapping: {
- undefined: [undefined],
- ASSET_MODEL: ['ASSET_MODEL'],
- COMPONENT_MODEL: ['COMPONENT_MODEL'],
- },
- },
},
} satisfies Meta;
-type AssetModelExplorerStoryControls =
- ResourceExplorerStoryControls &
- Pick;
+type AssetModelExplorerStory = (
+ controls: AssetModelExplorerStoryControls,
+ context: AssetModelExplorerStoryContext
+) => StoryFnReactReturnType;
+
+type AssetModelExplorerStoryControls = CommonResourceExplorerControls;
-export function StandardExample({
- isTitleEnabled,
- isFilterEnabled,
- isUserSettingsEnabled,
- ...assetModelExplorerProps
-}: AssetModelExplorerStoryControls) {
- const [selectedAssetModels, setSelectedAssetModels] = useState<
- NonNullable
- >([]);
+interface AssetModelExplorerStoryContext {
+ selectedResources: NonNullable<
+ AssetModelExplorerProps['selectedAssetModels']
+ >;
+ onSelectResource: NonNullable;
+}
+
+function storyArgsToProps(
+ {
+ isTableTitleEnabled,
+ isTableSearchEnabled,
+ isTableFilterEnabled,
+ isTableUserSettingsEnabled,
+ isDropDownFilterEnabled,
+ ...controls
+ }: AssetModelExplorerStoryControls,
+ { selectedResources, onSelectResource }: AssetModelExplorerStoryContext
+): AssetModelExplorerProps {
+ return {
+ selectedAssetModels: selectedResources,
+ onSelectAssetModel: onSelectResource,
+ tableSettings: {
+ isTitleEnabled: isTableTitleEnabled,
+ isSearchEnabled: isTableSearchEnabled,
+ isFilterEnabled: isTableFilterEnabled,
+ isUserSettingsEnabled: isTableUserSettingsEnabled,
+ },
+ dropDownSettings: {
+ isFilterEnabled: isDropDownFilterEnabled,
+ },
+ ...controls,
+ };
+}
+
+export const AssetAndComponentModels: AssetModelExplorerStory = (
+ controls,
+ context
+) => {
+ const props = storyArgsToProps(controls, context);
+
+ return ;
+};
+
+export const OnlyAssetModels: AssetModelExplorerStory = (controls, context) => {
+ const props = storyArgsToProps(controls, context);
return (
- <>
-
-
- >
+
);
-}
+};
+
+export const OnlyComponentModels: AssetModelExplorerStory = (
+ controls,
+ context
+) => {
+ const props = storyArgsToProps(controls, context);
-export function ZeroConfigurationTable() {
+ return (
+
+ );
+};
+
+export const ZeroConfigurationTable: AssetModelExplorerStory = () => {
return ;
-}
+};
-export function ZeroConfigurationDropDown() {
+export const ZeroConfigurationDropDown: AssetModelExplorerStory = () => {
return ;
-}
+};
diff --git a/packages/react-components/stories/resource-explorers/asset-property-explorer.stories.tsx b/packages/react-components/stories/resource-explorers/asset-property-explorer.stories.tsx
index bd18006d3..d59704a1d 100644
--- a/packages/react-components/stories/resource-explorers/asset-property-explorer.stories.tsx
+++ b/packages/react-components/stories/resource-explorers/asset-property-explorer.stories.tsx
@@ -1,78 +1,117 @@
import { type Meta } from '@storybook/react';
-import React, { useState } from 'react';
-import { SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES } from './constants';
+import React from 'react';
+import {
+ CommonResourceExplorerControls,
+ SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES,
+} from './controls';
import { client } from './data-source';
import {
AssetPropertyExplorer,
type AssetPropertyExplorerProps,
} from '../../src/components/resource-explorers';
-import { ResourceExplorerStoryControls } from './types';
-import type { AssetPropertyResource } from '../../src/components/resource-explorers/types/resources';
+import { StoryFnReactReturnType } from '@storybook/react/dist/ts3.9/client/preview/types';
+import {
+ StoryWithClearedResourceCache,
+ StoryWithSelectableResource,
+ StoryWithTanstackDevTools,
+} from './decorators';
export default {
title: 'Resource Explorers/Asset Property Explorer',
component: AssetPropertyExplorer,
+ parameters: {
+ controls: {
+ expanded: true,
+ },
+ },
+ decorators: [
+ StoryWithTanstackDevTools,
+ StoryWithClearedResourceCache,
+ StoryWithSelectableResource,
+ ],
argTypes: {
...SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES,
},
} satisfies Meta;
-type AssetPropertyExplorerStoryControls =
- ResourceExplorerStoryControls;
+type AssetPropertyExplorerStory = (
+ controls: AssetPropertyExplorerStoryControls,
+ context: AssetPropertyExplorerStoryContext
+) => StoryFnReactReturnType;
+
+type AssetPropertyExplorerStoryControls = CommonResourceExplorerControls;
+
+interface AssetPropertyExplorerStoryContext {
+ selectedResources: NonNullable<
+ AssetPropertyExplorerProps['selectedAssetProperties']
+ >;
+ onSelectResource: NonNullable<
+ AssetPropertyExplorerProps['onSelectAssetProperty']
+ >;
+}
+
+function storyArgsToProps(
+ {
+ isTableTitleEnabled,
+ isTableSearchEnabled,
+ isTableFilterEnabled,
+ isTableUserSettingsEnabled,
+ isDropDownFilterEnabled,
+ ...controls
+ }: AssetPropertyExplorerStoryControls,
+ { selectedResources, onSelectResource }: AssetPropertyExplorerStoryContext
+): AssetPropertyExplorerProps {
+ return {
+ selectedAssetProperties: selectedResources,
+ onSelectAssetProperty: onSelectResource,
+ tableSettings: {
+ isTitleEnabled: isTableTitleEnabled,
+ isSearchEnabled: isTableSearchEnabled,
+ isFilterEnabled: isTableFilterEnabled,
+ isUserSettingsEnabled: isTableUserSettingsEnabled,
+ },
+ dropDownSettings: {
+ isFilterEnabled: isDropDownFilterEnabled,
+ },
+ ...controls,
+ };
+}
-export function AssetPropertyExplorerExample({
- isTitleEnabled,
- isSearchEnabled,
- isFilterEnabled,
- isUserSettingsEnabled,
- ...assetPropertyExplorerProps
-}: AssetPropertyExplorerStoryControls) {
- const [selectedAssetProperties, setSelectedAssetProperties] = useState<
- NonNullable
- >([]);
+// Requires setting parameters manually (or using search)
+export const WithLatestValues: AssetPropertyExplorerStory = (
+ controls,
+ context
+) => {
+ const props = storyArgsToProps(controls, context);
+
+ return (
+
+ );
+};
+
+// Requires setting parameters manually (or using search)
+export const WithoutLatestValues: AssetPropertyExplorerStory = (
+ controls,
+ context
+) => {
+ const props = storyArgsToProps(controls, context);
return (
);
-}
+};
-export function ZeroConfiguration() {
+export const ZeroConfigurationTable: AssetPropertyExplorerStory = () => {
return ;
-}
+};
+
+export const ZeroConfigurationDropDown: AssetPropertyExplorerStory = () => {
+ return ;
+};
diff --git a/packages/react-components/stories/resource-explorers/constants.ts b/packages/react-components/stories/resource-explorers/constants.ts
deleted file mode 100644
index c45ed5c75..000000000
--- a/packages/react-components/stories/resource-explorers/constants.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import type { Meta } from '@storybook/react';
-import type { ResourceExplorerStoryControls } from './types';
-
-export const SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES = {
- selectionMode: {
- control: { type: 'radio' },
- options: [undefined, 'single', 'multi'],
- defaultValue: undefined,
- },
-
- isFilterEnabled: {
- control: { type: 'boolean' },
- defaultValue: false,
- },
-
- isUserSettingsEnabled: {
- control: { type: 'boolean' },
- defaultValue: false,
- },
-
- isSearchEnabled: {
- control: { type: 'boolean' },
- defaultValue: false,
- },
-
- isTitleEnabled: { control: { type: 'boolean' }, defaultValue: true },
-
- shouldPersistUserCustomization: {
- control: { type: 'boolean' },
- defaultValue: false,
- },
-
- defaultPageSize: {
- control: { type: 'radio' },
- options: [10, 25, 100, 250],
- defaultValue: 10,
- },
-
- variant: {
- control: { type: 'radio' },
- options: ['table', 'drop-down'],
- defaultValue: 'table',
- },
-} satisfies Meta>['argTypes'];
diff --git a/packages/react-components/stories/resource-explorers/controls.ts b/packages/react-components/stories/resource-explorers/controls.ts
new file mode 100644
index 000000000..f4c258619
--- /dev/null
+++ b/packages/react-components/stories/resource-explorers/controls.ts
@@ -0,0 +1,78 @@
+import type { Meta } from '@storybook/react';
+import type {
+ IsTableFilterEnabled,
+ IsTableSearchEnabled,
+ IsTableUserSettingsEnabled,
+ IsTitleEnabled,
+ PageSize,
+ ResourceExplorerVariant,
+ SelectionMode,
+ ShouldPersistUserCustomization,
+} from '../../src/components/resource-explorers/types/common';
+import { IsDropDownFilterEnabled } from '../../src/components/resource-explorers/types/drop-down';
+
+export interface CommonResourceExplorerControls {
+ selectionMode: SelectionMode;
+ shouldPersistUserCustomization: ShouldPersistUserCustomization;
+ defaultPageSize: PageSize;
+ variant: ResourceExplorerVariant;
+ isTableTitleEnabled: IsTitleEnabled;
+ isTableSearchEnabled: IsTableSearchEnabled;
+ isTableFilterEnabled: IsTableFilterEnabled;
+ isTableUserSettingsEnabled: IsTableUserSettingsEnabled;
+ isDropDownFilterEnabled: IsDropDownFilterEnabled;
+}
+
+export const SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES = {
+ selectionMode: {
+ control: { type: 'radio' },
+ options: [undefined, 'single', 'multi'],
+ defaultValue: undefined,
+ },
+ defaultPageSize: {
+ control: { type: 'radio' },
+ options: [10, 25, 100, 250],
+ defaultValue: 10,
+ },
+ shouldPersistUserCustomization: {
+ control: { type: 'boolean' },
+ defaultValue: false,
+ description: 'Specify if user settings should be stored by the browser.',
+ },
+ variant: {
+ control: { type: 'radio' },
+ options: ['table', 'drop-down'],
+ defaultValue: 'table',
+ },
+ isTableTitleEnabled: {
+ name: 'tableSettings.isTitleEnabled',
+ control: { type: 'boolean' },
+ defaultValue: true,
+ if: { arg: 'variant', eq: 'table' },
+ },
+ isTableSearchEnabled: {
+ name: 'tableSettings.isSearchEnabled',
+ control: { type: 'boolean' },
+ defaultValue: false,
+ if: { arg: 'variant', eq: 'table' },
+ },
+ isTableFilterEnabled: {
+ name: 'tableSettings.isFilterEnabled',
+ control: { type: 'boolean' },
+ defaultValue: false,
+ if: { arg: 'variant', eq: 'table' },
+ },
+ isTableUserSettingsEnabled: {
+ name: 'tableSettings.isUserSettingsEnabled',
+ control: { type: 'boolean' },
+ defaultValue: false,
+ if: { arg: 'variant', eq: 'table' },
+ },
+ isDropDownFilterEnabled: {
+ name: 'dropDownSettings.isFilterEnabled',
+ control: { type: 'boolean' },
+ defaultValue: false,
+ description: 'Enable the drop-drop down filter',
+ if: { arg: 'variant', eq: 'drop-down' },
+ },
+} satisfies Meta['argTypes'];
diff --git a/packages/react-components/stories/resource-explorers/decorators.tsx b/packages/react-components/stories/resource-explorers/decorators.tsx
new file mode 100644
index 000000000..7f0cb11ec
--- /dev/null
+++ b/packages/react-components/stories/resource-explorers/decorators.tsx
@@ -0,0 +1,32 @@
+import { type DecoratorFn } from '@storybook/react';
+import React, { useEffect, useState } from 'react';
+import { resourceExplorerQueryClient } from '../../src/components/resource-explorers/requests';
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+
+export const StoryWithTanstackDevTools: DecoratorFn = (Story) => {
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export const StoryWithClearedResourceCache: DecoratorFn = (Story) => {
+ useEffect(() => {
+ resourceExplorerQueryClient.clear();
+ }, []);
+
+ return ;
+};
+
+export const StoryWithSelectableResource: DecoratorFn = (Story) => {
+ const [selectedResources, setSelectedResources] = useState([]);
+
+ return (
+
+ );
+};
diff --git a/packages/react-components/stories/resource-explorers/explorer-combinations.stories.tsx b/packages/react-components/stories/resource-explorers/explorer-combinations.stories.tsx
index b9f858d3d..e1620a39f 100644
--- a/packages/react-components/stories/resource-explorers/explorer-combinations.stories.tsx
+++ b/packages/react-components/stories/resource-explorers/explorer-combinations.stories.tsx
@@ -1,32 +1,48 @@
import { type Meta } from '@storybook/react';
import React, { useState } from 'react';
-import { SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES } from './constants';
+import {
+ StoryWithClearedResourceCache,
+ StoryWithTanstackDevTools,
+} from './decorators';
import { client } from './data-source';
-
import {
AssetModelExplorer,
AssetExplorer,
AssetPropertyExplorer,
+ TimeSeriesExplorer,
type AssetModelExplorerProps,
type AssetExplorerProps,
- type AssetPropertyExplorerProps,
} from '../../src/components/resource-explorers';
export default {
title: 'Resource Explorers/Combinations',
component: AssetExplorer,
- argTypes: {
- ...SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES,
- },
+ decorators: [StoryWithTanstackDevTools, StoryWithClearedResourceCache],
} satisfies Meta;
-export function AssetPlusAssetPropertyExplorer() {
+export function AssetExplorerPlusAssetPropertyExplorer() {
const [selectedAssets, setSelectedAssets] = useState<
NonNullable
>([]);
- const [selectedAssetProperties, setSelectedAssetProperties] = useState<
- NonNullable
+
+ return (
+ <>
+
+
+
+ >
+ );
+}
+
+export function AssetExplorerPlusTimeSeriesExplorer() {
+ const [selectedAssets, setSelectedAssets] = useState<
+ NonNullable
>([]);
return (
@@ -36,20 +52,78 @@ export function AssetPlusAssetPropertyExplorer() {
onSelectAsset={setSelectedAssets}
selectedAssets={selectedAssets}
selectionMode='multi'
+ tableSettings={{
+ isSearchEnabled: true,
+ isFilterEnabled: true,
+ isUserSettingsEnabled: true,
+ }}
/>
-
+ >
+ );
+}
+
+export function AssetExplorerPlusAssetExplorer() {
+ const [selectedAssets, setSelectedAssets] = useState<
+ NonNullable
+ >([]);
+
+ return (
+ <>
+
+
+
+ >
+ );
+}
+
+export function AssetModelExplorerPlusAssetExplorer() {
+ const [selectedAssetModels, setSelectedAssetModels] = useState<
+ NonNullable
+ >([]);
+
+ return (
+ <>
+
+
+
>
);
}
-export function AssetModelPlusAssetExplorer() {
+export function AssetModelExplorerPlusAssetExplorerPlusAssetPropertyExplorer() {
const [selectedAssetModels, setSelectedAssetModels] = useState<
NonNullable
>([]);
@@ -60,19 +134,80 @@ export function AssetModelPlusAssetExplorer() {
return (
<>
+
+
+ >
+ );
+}
+
+export function AssetModelExplorerPlusAssetExplorerPlusTimeSeriesExplorer() {
+ const [selectedAssetModels, setSelectedAssetModels] = useState<
+ NonNullable
+ >([]);
+ const [selectedAssets, setSelectedAssets] = useState<
+ NonNullable
+ >([]);
+
+ return (
+ <>
+
+
+
+
+
>
);
diff --git a/packages/react-components/stories/resource-explorers/time-series-explorer.stories.tsx b/packages/react-components/stories/resource-explorers/time-series-explorer.stories.tsx
index 02dbfeb15..88d0350e7 100644
--- a/packages/react-components/stories/resource-explorers/time-series-explorer.stories.tsx
+++ b/packages/react-components/stories/resource-explorers/time-series-explorer.stories.tsx
@@ -1,106 +1,138 @@
import { type Meta } from '@storybook/react';
-import React, { useState } from 'react';
+import React from 'react';
-import { SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES } from './constants';
+import {
+ CommonResourceExplorerControls,
+ SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES,
+} from './controls';
import { client } from './data-source';
import {
TimeSeriesExplorer,
type TimeSeriesExplorerProps,
} from '../../src/components/resource-explorers';
-import type { ResourceExplorerStoryControls } from './types';
-import type { TimeSeriesResource } from '../../src/components/resource-explorers/types/resources';
+import { StoryFnReactReturnType } from '@storybook/react/dist/ts3.9/client/preview/types';
+import {
+ StoryWithClearedResourceCache,
+ StoryWithSelectableResource,
+ StoryWithTanstackDevTools,
+} from './decorators';
export default {
title: 'Resource Explorers/Time Series Explorer',
component: TimeSeriesExplorer,
+ parameters: {
+ controls: {
+ expanded: true,
+ exclude: ['tableSettings.isSearchEnabled'],
+ },
+ },
+ decorators: [
+ StoryWithTanstackDevTools,
+ StoryWithClearedResourceCache,
+ StoryWithSelectableResource,
+ ],
argTypes: {
...SHARED_RESOURCE_EXPLORER_STORY_ARG_TYPES,
},
} satisfies Meta;
-type TimeSeriesExplorerStoryControls =
- ResourceExplorerStoryControls &
- Pick;
+type TimeSeriesExplorerStory = (
+ controls: TimeSeriesExplorerStoryControls,
+ context: AssetPropertyExplorerStoryContext
+) => StoryFnReactReturnType;
+
+type TimeSeriesExplorerStoryControls = CommonResourceExplorerControls;
+
+interface AssetPropertyExplorerStoryContext {
+ selectedResources: NonNullable;
+ onSelectResource: NonNullable;
+}
+
+function storyArgsToProps(
+ {
+ isTableTitleEnabled,
+ isTableSearchEnabled,
+ isTableFilterEnabled,
+ isTableUserSettingsEnabled,
+ isDropDownFilterEnabled,
+ ...controls
+ }: TimeSeriesExplorerStoryControls,
+ { selectedResources, onSelectResource }: AssetPropertyExplorerStoryContext
+): TimeSeriesExplorerProps {
+ return {
+ selectedTimeSeries: selectedResources,
+ onSelectTimeSeries: onSelectResource,
+ tableSettings: {
+ isTitleEnabled: isTableTitleEnabled,
+ isSearchEnabled: isTableSearchEnabled,
+ isFilterEnabled: isTableFilterEnabled,
+ isUserSettingsEnabled: isTableUserSettingsEnabled,
+ },
+ dropDownSettings: {
+ isFilterEnabled: isDropDownFilterEnabled,
+ },
+ ...controls,
+ };
+}
+
+export const AllTimeSeriesWithLatestValues: TimeSeriesExplorerStory = (
+ controls,
+ context
+) => {
+ const props = storyArgsToProps(controls, context);
+
+ return ;
+};
-export function AllTimeSeries({
- isTitleEnabled,
- isFilterEnabled,
- isUserSettingsEnabled,
- ...timeSeriesExplorerProps
-}: TimeSeriesExplorerStoryControls) {
- const [selectedTimeSeries, setSeletedTimeSeries] = useState<
- NonNullable
- >([]);
+export const AllTimeSeriesWithoutLatestValues: TimeSeriesExplorerStory = (
+ controls,
+ context
+) => {
+ const props = storyArgsToProps(controls, context);
return (
);
-}
+};
-export function AllAssociatedTimeSeries({
- isTitleEnabled,
- isFilterEnabled,
- isUserSettingsEnabled,
- ...timeSeriesExplorerProps
-}: TimeSeriesExplorerStoryControls) {
- const [selectedTimeSeries, setSeletedTimeSeries] = useState<
- NonNullable
- >([]);
+export const AllAssociatedTimeSeries: TimeSeriesExplorerStory = (
+ controls,
+ context
+) => {
+ const props = storyArgsToProps(controls, context);
return (
);
-}
+};
-export function AllDisassociatedTimeSeries({
- isTitleEnabled,
- isFilterEnabled,
- isUserSettingsEnabled,
- ...timeSeriesExplorerProps
-}: TimeSeriesExplorerStoryControls) {
- const [selectedTimeSeries, setSeletedTimeSeries] = useState<
- NonNullable
- >([]);
+export const AllDisassociatedTimeSeries: TimeSeriesExplorerStory = (
+ controls,
+ context
+) => {
+ const props = storyArgsToProps(controls, context);
return (
);
-}
+};
-export function ZeroConfiguration() {
+export const ZeroConfigurationTable: TimeSeriesExplorerStory = () => {
return ;
-}
+};
+
+export const ZeroConfigurationDropDown: TimeSeriesExplorerStory = () => {
+ return ;
+};
diff --git a/packages/react-components/stories/resource-explorers/types.ts b/packages/react-components/stories/resource-explorers/types.ts
deleted file mode 100644
index 98a647a87..000000000
--- a/packages/react-components/stories/resource-explorers/types.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import type { CommonResourceExplorerProps } from '../../src/components/resource-explorers/types/resource-explorer';
-
-export type ResourceExplorerStoryControls = Pick<
- CommonResourceExplorerProps,
- | 'variant'
- | 'defaultPageSize'
- | 'shouldPersistUserCustomization'
- | 'selectionMode'
-> &
- CommonResourceExplorerProps['tableSettings'];