redux-devtools/packages/redux-devtools-rtk-query-monitor/src/containers/QueryPreview.tsx
Raosandeep007 193141bf52 Query actions and copy data to clipboard
- RTK query Refresh, Loading, Fulfill actions.
- Copy button to copy json data.
2025-08-27 13:29:39 +05:30

260 lines
6.9 KiB
TypeScript

import React, { ReactNode } from 'react';
import type { Interpolation, Theme } from '@emotion/react';
import { Dispatch, AnyAction } from 'redux';
import {
QueryPreviewTabs,
RtkResourceInfo,
SelectorsSource,
TabOption,
} from '../types';
import { QueryPreviewHeader } from '../components/QueryPreviewHeader';
import {
QueryPreviewInfo,
QueryPreviewInfoProps,
} from '../components/QueryPreviewInfo';
import {
QueryPreviewApi,
QueryPreviewApiProps,
} from '../components/QueryPreviewApi';
import {
QueryPreviewSubscriptions,
QueryPreviewSubscriptionsProps,
} from '../components/QueryPreviewSubscriptions';
import {
QueryPreviewTags,
QueryPreviewTagsProps,
} from '../components/QueryPreviewTags';
import { NoRtkQueryApi } from '../components/NoRtkQueryApi';
import { InspectorSelectors } from '../selectors';
import { mapProps } from './mapProps';
import {
QueryPreviewActions,
QueryPreviewActionsProps,
} from '../components/QueryPreviewActions';
import { isTabVisible } from '../utils/tabs';
import {
QueryPreviewData,
QueryPreviewDataProps,
} from '../components/QueryPreviewData';
const queryPreviewCss: Interpolation<Theme> = (theme) => ({
flex: '1 1 50%',
overflowX: 'hidden',
oveflowY: 'auto',
display: 'flex',
flexDirection: 'column',
overflowY: 'hidden',
'& pre': {
border: 'inherit',
borderRadius: '3px',
lineHeight: 'inherit',
color: 'inherit',
},
backgroundColor: theme.BACKGROUND_COLOR,
});
export interface QueryPreviewProps<S = unknown> {
readonly selectedTab: QueryPreviewTabs;
readonly hasNoApis: boolean;
readonly onTabChange: (tab: QueryPreviewTabs) => void;
readonly resInfo: RtkResourceInfo | null;
readonly isWideLayout: boolean;
readonly selectorsSource: SelectorsSource<S>;
readonly selectors: InspectorSelectors<S>;
readonly dispatch?: Dispatch<AnyAction>;
readonly liftedDispatch?: Dispatch<AnyAction>;
}
/**
* Tab content is not rendered if there's no selected query.
*/
type QueryPreviewTabProps = Omit<QueryPreviewProps<unknown>, 'resInfo'> & {
resInfo: RtkResourceInfo;
};
const MappedQueryPreviewTags = mapProps<
QueryPreviewTabProps,
QueryPreviewTagsProps
>(({ selectors, selectorsSource, isWideLayout, resInfo }) => ({
resInfo,
tags: selectors.selectCurrentQueryTags(selectorsSource),
isWideLayout,
}))(QueryPreviewTags);
const MappedQueryPreviewInfo = mapProps<
QueryPreviewTabProps,
QueryPreviewInfoProps
>(({ resInfo, isWideLayout }) => ({ resInfo, isWideLayout }))(QueryPreviewInfo);
const MappedQueryPreviewData = mapProps<
QueryPreviewTabProps,
QueryPreviewDataProps
>(({ resInfo, isWideLayout }) => ({
data: resInfo?.state?.data,
isWideLayout,
}))(QueryPreviewData);
const MappedQuerySubscriptipns = mapProps<
QueryPreviewTabProps,
QueryPreviewSubscriptionsProps
>(({ selectors, selectorsSource, isWideLayout }) => ({
isWideLayout,
subscriptions: selectors.selectSubscriptionsOfCurrentQuery(selectorsSource),
}))(QueryPreviewSubscriptions);
const MappedApiPreview = mapProps<QueryPreviewTabProps, QueryPreviewApiProps>(
({ isWideLayout, selectors, selectorsSource }) => ({
isWideLayout,
apiState: selectors.selectApiOfCurrentQuery(selectorsSource),
apiStats: selectors.selectApiStatsOfCurrentQuery(selectorsSource),
}),
)(QueryPreviewApi);
const MappedQueryPreviewActions = mapProps<
QueryPreviewTabProps,
QueryPreviewActionsProps
>(({ isWideLayout, selectorsSource, selectors, resInfo, ...props }) => ({
isWideLayout,
actionsOfQuery: selectors.selectActionsOfCurrentQuery(selectorsSource),
dispatch: (props as any).dispatch,
liftedDispatch: (props as any).liftedDispatch,
resInfo,
}))(QueryPreviewActions);
const tabs: ReadonlyArray<
TabOption<QueryPreviewTabs, QueryPreviewTabProps, RtkResourceInfo['type']>
> = [
{
label: 'Data',
value: QueryPreviewTabs.data,
component: MappedQueryPreviewData,
visible: {
query: true,
mutation: true,
default: true,
},
},
{
label: 'Query',
value: QueryPreviewTabs.queryinfo,
component: MappedQueryPreviewInfo,
visible: {
query: true,
mutation: true,
default: true,
},
},
{
label: 'Actions',
value: QueryPreviewTabs.actions,
component: MappedQueryPreviewActions,
visible: {
query: true,
mutation: true,
default: true,
},
},
{
label: 'Tags',
value: QueryPreviewTabs.queryTags,
component: MappedQueryPreviewTags,
visible: {
query: true,
mutation: false,
default: true,
},
},
{
label: 'Subs',
value: QueryPreviewTabs.querySubscriptions,
component: MappedQuerySubscriptipns,
visible: {
query: true,
mutation: false,
default: true,
},
},
{
label: 'Api',
value: QueryPreviewTabs.apiConfig,
component: MappedApiPreview,
},
];
export class QueryPreview<S> extends React.PureComponent<QueryPreviewProps<S>> {
renderLabelWithCounter = (label: string, counter: number): string => {
let counterAsString = counter.toFixed(0);
if (counterAsString.length > 3) {
counterAsString = counterAsString.slice(0, 2) + '...';
}
return `${label}(${counterAsString})`;
};
renderTabLabel = (
tab: TabOption<QueryPreviewTabs, unknown, 'query' | 'mutation'>,
): ReactNode => {
const { selectors, selectorsSource, resInfo } = this.props;
const tabCount = selectors.selectTabCounters(selectorsSource)[tab.value];
let tabLabel = tab.label;
if (tabLabel === 'query' && resInfo?.type === 'mutation') {
tabLabel = resInfo.type;
}
if (tabCount > 0) {
return this.renderLabelWithCounter(tabLabel, tabCount);
}
return tabLabel;
};
render(): ReactNode {
const { resInfo, selectedTab, onTabChange, hasNoApis } = this.props;
const { component: TabComponent } =
tabs.find((tab) => tab.value === selectedTab) || tabs[0];
if (!resInfo) {
return (
<div css={queryPreviewCss}>
<QueryPreviewHeader
selectedTab={selectedTab}
onTabChange={onTabChange}
tabs={
tabs.filter((tab) =>
isTabVisible(tab, 'default'),
) as ReadonlyArray<
TabOption<QueryPreviewTabs, unknown, RtkResourceInfo['type']>
>
}
renderTabLabel={this.renderTabLabel}
/>
{hasNoApis && <NoRtkQueryApi />}
</div>
);
}
return (
<div css={queryPreviewCss}>
<QueryPreviewHeader
selectedTab={selectedTab}
onTabChange={onTabChange}
tabs={
tabs.filter((tab) =>
isTabVisible(tab, resInfo.type),
) as ReadonlyArray<
TabOption<QueryPreviewTabs, unknown, RtkResourceInfo['type']>
>
}
renderTabLabel={this.renderTabLabel}
/>
<TabComponent {...(this.props as QueryPreviewTabProps)} />
</div>
);
}
}