Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix loader logic in group controls #1236

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ type ControlProps = {
needReload: boolean;
workbookId?: WorkbookId;
dependentSelectors?: boolean;
autoUpdating: boolean;
};

export const Control = ({
Expand All @@ -110,6 +111,7 @@ export const Control = ({
needReload,
workbookId,
dependentSelectors,
autoUpdating,
}: ControlProps) => {
const [prevNeedReload, setPrevNeedReload] = React.useState(needReload);
const isMounted = useMountedState([]);
Expand Down Expand Up @@ -397,7 +399,11 @@ export const Control = ({
};

const renderSilentLoader = () => {
if (showSilentLoader || (!control && status === LOAD_STATUS.SUCCESS)) {
if (
showSilentLoader ||
(!control && status === LOAD_STATUS.SUCCESS) ||
(autoUpdating && status === LOAD_STATUS.PENDING)
) {
return (
<div className={b('loader', {silent: true})}>
<Loader size="s" />
Expand Down
46 changes: 31 additions & 15 deletions src/ui/components/DashKit/plugins/GroupControl/GroupControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React from 'react';
import type {Plugin, PluginWidgetProps, SettingsProps} from '@gravity-ui/dashkit';
import type {Config, StateAndParamsMetaData} from '@gravity-ui/dashkit/helpers';
import {getItemsParams, pluginGroupControlBaseDL} from '@gravity-ui/dashkit/helpers';
import {Loader} from '@gravity-ui/uikit';
import block from 'bem-cn-lite';
import {I18n} from 'i18n';
import debounce from 'lodash/debounce';
Expand Down Expand Up @@ -36,6 +35,7 @@ import type {ControlSettings, GetDistincts, LoadStatus} from '../Control/types';
import DebugInfoTool from '../DebugInfoTool/DebugInfoTool';

import {Control} from './Control/Control';
import {LocalUpdateLoader} from './LocalUpdateLoader/LocalUpdateLoader';
import type {
ContextProps,
ExtendedLoadedData,
Expand All @@ -48,6 +48,7 @@ import {addItemToLocalQueue, filterSignificantParams} from './utils';
import './GroupControl.scss';

const GROUP_CONTROL_LAYOUT_DEBOUNCE_TIME = 20;
const LOADER_HIDE_DELAY = 500;

type StateProps = ReturnType<typeof mapStateToProps>;

Expand Down Expand Up @@ -87,6 +88,8 @@ class GroupControl extends React.PureComponent<PluginGroupControlProps, PluginGr
// a quick loader for imitating action by clicking on apply button
quickActionLoader = false;

autoUpdating = false;

// params of current dash state
initialParams: Record<string, StringParams> = {};

Expand Down Expand Up @@ -124,7 +127,6 @@ class GroupControl extends React.PureComponent<PluginGroupControlProps, PluginGr
isInit: false,
stateParams,
needReload: false,
localUpdateLoader: false,
};
}

Expand Down Expand Up @@ -225,9 +227,9 @@ class GroupControl extends React.PureComponent<PluginGroupControlProps, PluginGr

render() {
const isLoading =
(this.state.status === LOAD_STATUS.PENDING && !this.state.silentLoading) ||
this.quickActionLoader ||
this.state.localUpdateLoader;
((this.state.status === LOAD_STATUS.PENDING && !this.state.silentLoading) ||
this.quickActionLoader) &&
!this.autoUpdating;

return (
<div
Expand All @@ -244,16 +246,23 @@ class GroupControl extends React.PureComponent<PluginGroupControlProps, PluginGr
modType="bottom-right-corner"
/>
{this.renderControls()}
{isLoading && (
<div className={b('loader')}>
<Loader size="s" qa={ControlQA.groupCommonLoader} />
</div>
)}
<LocalUpdateLoader
hideDelay={LOADER_HIDE_DELAY}
size="s"
className={b('loader')}
show={isLoading}
qa={ControlQA.groupCommonLoader}
onLoaderShow={this.handleLoaderShow}
/>
</div>
</div>
);
}

private handleLoaderShow = () => {
this.quickActionLoader = false;
};

private get dependentSelectors() {
return this.props.settings.dependentSelectors ?? false;
}
Expand Down Expand Up @@ -417,8 +426,8 @@ class GroupControl extends React.PureComponent<PluginGroupControlProps, PluginGr
if (controlData.updateControlsOnChange && controlData.buttonApply) {
this.setState({
stateParams: this.getLocalUpdatedParams(controlId, params),
localUpdateLoader: true,
});
this.autoUpdating = true;
} else {
this.setState({
stateParams: {
Expand Down Expand Up @@ -651,6 +660,15 @@ class GroupControl extends React.PureComponent<PluginGroupControlProps, PluginGr
this.controlsProgressCount++;
}

// to make loader be visible
if (
this.controlsProgressCount &&
this.state.status !== LOAD_STATUS.PENDING &&
(this.props.data as unknown as DashTabItemGroupControlData).updateControlsOnChange
) {
this.setState({status: LOAD_STATUS.PENDING});
}

if (!this.controlsProgressCount) {
// adjust widget layout only for the first loading of widget
if (this.props.data.autoHeight && !this.state.isInit) {
Expand All @@ -663,7 +681,6 @@ class GroupControl extends React.PureComponent<PluginGroupControlProps, PluginGr
status: LOAD_STATUS.SUCCESS,
silentLoading: false,
isInit: true,
localUpdateLoader: false,
});
}

Expand All @@ -687,6 +704,7 @@ class GroupControl extends React.PureComponent<PluginGroupControlProps, PluginGr
needReload={this.state.needReload}
workbookId={workbookId}
dependentSelectors={this.dependentSelectors}
autoUpdating={this.autoUpdating}
/>
);
}
Expand Down Expand Up @@ -732,10 +750,8 @@ class GroupControl extends React.PureComponent<PluginGroupControlProps, PluginGr
!isEqual(newParams, this.props.params)
) {
if (action === CLICK_ACTION_TYPE.SET_PARAMS) {
this.autoUpdating = false;
this.quickActionLoader = true;
setTimeout(() => {
this.quickActionLoader = false;
});
}
this.onChange({params: newParams, callChangeByClick});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';

import type {LoaderSize} from '@gravity-ui/uikit';
import {Loader} from '@gravity-ui/uikit';

export const LocalUpdateLoader = ({
show,
hideDelay,
className,
size,
qa,
onLoaderShow,
}: {
show: boolean;
hideDelay: number;
className?: string;
size: LoaderSize;
qa?: string;
onLoaderShow?: () => void;
}) => {
const [prevShow, setPrevShow] = React.useState(show);
const [showByTimer, setShowByDelay] = React.useState(false);
const [showByCondition, setShowByCondition] = React.useState(false);

if (show !== prevShow) {
setPrevShow(show);

if (show) {
onLoaderShow?.();
setShowByCondition(true);
setShowByDelay(true);

if (hideDelay) {
window.setTimeout(() => {
setShowByDelay(false);
}, hideDelay);
}
}
}

if (!show && showByCondition) {
setShowByCondition(false);
}

if (showByCondition || showByTimer) {
return <Loader size={size} className={className} qa={qa} />;
}
return null;
};
1 change: 0 additions & 1 deletion src/ui/components/DashKit/plugins/GroupControl/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export interface PluginGroupControlState {
isInit: boolean;
stateParams: Record<string, StringParams>;
needReload: boolean;
localUpdateLoader: boolean;
}

export type ResolveMetaResult = {
Expand Down
27 changes: 6 additions & 21 deletions src/ui/libs/DatalensChartkit/components/Control/Items/Items.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,22 +176,18 @@ function BaseControlInput({

const isInvalid = hasValidationError && !text?.length;

const handleBlur = () => {
onChange(text);
};

return (
<TextInput
placeholder={placeholder}
className={b('component', {input: true})}
value={text}
onUpdate={(value) => setText(value)}
onKeyPress={(event) => event.charCode === 13 && onChange(text)}
onBlur={() => {
// For case: input has `updateOnChange: true`, there is button control with `setInitialParams`.
// Need setTimeout for common microtask queue: firstly fire onBlur (from Input), then onClick (from Button)
// Before fix: in some cases onClick form Button didn't fire at all (or just didn't work, next work)
// (Possible reasons: because of rerendering after onChange form Input).
setTimeout(() => {
onChange(text);
});
}}
onBlur={handleBlur}
label={labelInside ? label : innerLabel}
qa={ControlQA.controlInput}
// triggered twice, so controlAttrs.onKeyPress is used
Expand Down Expand Up @@ -468,19 +464,8 @@ function BaseControlButton({label, theme, onChange, qa}) {

const size = DL.IS_MOBILE ? MOBILE_SIZE.BUTTON : 's';

const handleClick = () => {
setTimeout(onChange);
};

return (
<Button
view={buttonTheme || 'normal'}
size={size}
width="max"
// Need setTimeout for common microtask queue: firstly fire onBlur (from Input), then onClick (from Button)
onClick={handleClick}
qa={qa}
>
<Button view={buttonTheme || 'normal'} size={size} width="max" onClick={onChange} qa={qa}>
{label || i18n('chartkit.control.items', 'apply')}
</Button>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ export const GroupControlSidebar = () => {
/>
</div>
<Checkbox
checked={selectorsGroup.updateControlsOnChange}
checked={selectorsGroup.updateControlsOnChange || false}
onUpdate={handleChangeUpdateControls}
size="l"
qa={DialogGroupControlQa.updateControlOnChangeCheckbox}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const SELECTORS_TITLES = {
],
FIRST_DATASET_SELECTOR: [PARAMS.DATASET_FIRST_CONTROL.appearance.title],
SECOND_MANUAL_SELECTOR: [PARAMS.MANUAL_SECOND_SELECTOR.appearance.title],
SECOND_DATASET_SELECTOR: [PARAMS.DATASET_SECOND_CONTROL.appearance.title],
};

const getSecondSelectItemsCount = async (dashboardPage: DashboardPage) => {
Expand Down Expand Up @@ -199,7 +200,7 @@ datalensTest.describe('Dashboards - Autoupdate options of group selectors', () =
expect(await getSecondSelectItemsCount(dashboardPage)).toEqual(cityItemsCount);

await dashboardPage.expectControlsRequests({
controlTitles: SELECTORS_TITLES.DATASET_SELECTORS,
controlTitles: SELECTORS_TITLES.SECOND_DATASET_SELECTOR,
action: async () => {
await page.locator(slct(ControlQA.controlButtonApply)).click();
},
Expand Down
Loading