This is an automated email from the ASF dual-hosted git repository. diegopucci pushed a commit to branch geido/feat/progressive-dashboard-header in repository https://gitbox.apache.org/repos/asf/superset.git
commit 3bdacded281f8869029df3a946a652092c2e4082 Author: Diego Pucci <[email protected]> AuthorDate: Tue Oct 15 18:02:19 2024 +0300 chore(Dashboard): Unblock header actions while loading --- .../src/components/FaveStar/index.tsx | 12 ++++++---- .../Header/HeaderActionsDropdown/index.jsx | 26 ++++++++++++++-------- .../src/dashboard/components/Header/index.jsx | 17 +++++++------- .../dashboard/components/PublishedStatus/index.jsx | 4 ++++ .../dashboard/components/RefreshIntervalModal.tsx | 25 +++++++++++++-------- 5 files changed, 54 insertions(+), 30 deletions(-) diff --git a/superset-frontend/src/components/FaveStar/index.tsx b/superset-frontend/src/components/FaveStar/index.tsx index fa7963480c..44f5a91cdd 100644 --- a/superset-frontend/src/components/FaveStar/index.tsx +++ b/superset-frontend/src/components/FaveStar/index.tsx @@ -24,7 +24,7 @@ import { Tooltip } from 'src/components/Tooltip'; import Icons from 'src/components/Icons'; export interface FaveStarProps { - itemId: number; + itemId?: number; isStarred?: boolean; showTooltip?: boolean; saveFaveStar(id: number, isStarred: boolean): any; @@ -47,13 +47,17 @@ const FaveStar = ({ fetchFaveStar, }: FaveStarProps) => { useEffect(() => { - fetchFaveStar?.(itemId); + if (itemId) { + fetchFaveStar?.(itemId); + } }, [fetchFaveStar, itemId]); const onClick = useCallback( (e: MouseEvent) => { - e.preventDefault(); - saveFaveStar(itemId, !!isStarred); + if (itemId) { + e.preventDefault(); + saveFaveStar(itemId, !!isStarred); + } }, [isStarred, itemId, saveFaveStar], ); diff --git a/superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/index.jsx b/superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/index.jsx index 50cdb67813..fe9b727336 100644 --- a/superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/index.jsx +++ b/superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/index.jsx @@ -40,7 +40,7 @@ import { MenuKeys } from 'src/dashboard/types'; const propTypes = { addSuccessToast: PropTypes.func.isRequired, addDangerToast: PropTypes.func.isRequired, - dashboardInfo: PropTypes.object.isRequired, + dashboardInfo: PropTypes.object, dashboardId: PropTypes.number, dashboardTitle: PropTypes.string, dataMask: PropTypes.object.isRequired, @@ -196,7 +196,7 @@ export class HeaderActionsDropdown extends PureComponent { }); const refreshIntervalOptions = - dashboardInfo.common?.conf?.DASHBOARD_AUTO_REFRESH_INTERVALS; + dashboardInfo?.common?.conf?.DASHBOARD_AUTO_REFRESH_INTERVALS; const dashboardComponentId = [...(directPathToChild || [])].pop(); @@ -216,6 +216,7 @@ export class HeaderActionsDropdown extends PureComponent { <Menu.Item key={MenuKeys.ToggleFullscreen} onClick={this.handleMenuClick} + disabled={isLoading} > {getUrlParam(URL_PARAMS.standalone) ? t('Exit fullscreen') @@ -226,23 +227,25 @@ export class HeaderActionsDropdown extends PureComponent { <Menu.Item key={MenuKeys.EditProperties} onClick={this.handleMenuClick} + disabled={isLoading} > {t('Edit properties')} </Menu.Item> )} {editMode && ( - <Menu.Item key={MenuKeys.EditCss}> + <Menu.Item key={MenuKeys.EditCss} disabled={isLoading}> <CssEditor triggerNode={<span>{t('Edit CSS')}</span>} initialCss={this.state.css} onChange={this.changeCss} addDangerToast={addDangerToast} + disabled={isLoading} /> </Menu.Item> )} <Menu.Divider /> {userCanSave && ( - <Menu.Item key={MenuKeys.SaveModal}> + <Menu.Item key={MenuKeys.SaveModal} disabled={isLoading}> <SaveModal addSuccessToast={this.props.addSuccessToast} addDangerToast={this.props.addDangerToast} @@ -303,6 +306,7 @@ export class HeaderActionsDropdown extends PureComponent { <Menu.Item key={MenuKeys.ManageEmbedded} onClick={this.handleMenuClick} + disabled={isLoading} > {t('Embed dashboard')} </Menu.Item> @@ -311,9 +315,12 @@ export class HeaderActionsDropdown extends PureComponent { {!editMode ? ( this.state.showReportSubMenu ? ( <> - <Menu.SubMenu title={t('Manage email report')}> + <Menu.SubMenu + title={t('Manage email report')} + disabled={isLoading} + > <HeaderReportDropdown - dashboardId={dashboardInfo.id} + dashboardId={dashboardInfo?.id} setShowReportSubMenu={this.setShowReportSubMenu} showReportSubMenu={this.state.showReportSubMenu} setIsDropdownVisible={setIsDropdownVisible} @@ -326,17 +333,18 @@ export class HeaderActionsDropdown extends PureComponent { ) : ( <Menu> <HeaderReportDropdown - dashboardId={dashboardInfo.id} + dashboardId={dashboardInfo?.id} setShowReportSubMenu={this.setShowReportSubMenu} setIsDropdownVisible={setIsDropdownVisible} isDropdownVisible={isDropdownVisible} useTextMenu + disabled={isLoading} /> </Menu> ) ) : null} {editMode && !isEmpty(dashboardInfo?.metadata?.filter_scopes) && ( - <Menu.Item key={MenuKeys.SetFilterMapping}> + <Menu.Item key={MenuKeys.SetFilterMapping} disabled={isLoading}> <FilterScopeModal className="m-r-5" triggerNode={t('Set filter mapping')} @@ -344,7 +352,7 @@ export class HeaderActionsDropdown extends PureComponent { </Menu.Item> )} - <Menu.Item key={MenuKeys.AutorefreshModal}> + <Menu.Item key={MenuKeys.AutorefreshModal} disabled={isLoading}> <RefreshIntervalModal addSuccessToast={this.props.addSuccessToast} refreshFrequency={refreshFrequency} diff --git a/superset-frontend/src/dashboard/components/Header/index.jsx b/superset-frontend/src/dashboard/components/Header/index.jsx index 9feb086bfc..e6b3b32b29 100644 --- a/superset-frontend/src/dashboard/components/Header/index.jsx +++ b/superset-frontend/src/dashboard/components/Header/index.jsx @@ -446,7 +446,8 @@ class Header extends PureComponent { }, { type: MetadataType.Owner, - createdBy: getOwnerName(dashboardInfo?.created_by) || t('Not available'), + createdBy: + getOwnerName(dashboardInfo?.created_by) || t('Not available'), owners: dashboardInfo?.owners.length > 0 ? dashboardInfo?.owners.map(getOwnerName) @@ -458,6 +459,9 @@ class Header extends PureComponent { render() { const { + dashboardInfo, + dashboardTitle, + chartsLoading, layout, expandedSlices, customCss, @@ -477,16 +481,12 @@ class Header extends PureComponent { isPublished, user, hasUnsavedChanges, - chartsLoading, refreshFrequency, shouldPersistRefreshFrequency, setRefreshFrequency, lastModifiedTime, logEvent, } = this.props; - const dashboardInfo = null; - const dashboardTitle = null; - const userCanEdit = dashboardInfo?.dash_edit_perm && !dashboardInfo?.is_managed_externally; const userCanShare = dashboardInfo?.dash_share_perm; @@ -544,7 +544,7 @@ class Header extends PureComponent { fetchFaveStar: this.props.fetchFaveStar, saveFaveStar: this.props.saveFaveStar, isStarred: this.props.isStarred, - showTooltip: true, + showTooltip: !!dashboardInfo?.id, }} titlePanelAdditionalItems={[ !editMode && ( @@ -555,13 +555,14 @@ class Header extends PureComponent { canEdit={userCanEdit} canSave={userCanSaveAs} visible={!editMode} + loading={!dashboardInfo?.id} /> ), !editMode && ( <MetadataBar items={this.getMetadataItems()} tooltipPlacement="bottom" - loading={!dashboardInfo} + loading={!dashboardInfo?.id} /> ), ]} @@ -701,7 +702,7 @@ class Header extends PureComponent { userCanShare={userCanShare} userCanSave={userCanSaveAs} userCanCurate={userCanCurate} - isLoading={chartsLoading} + isLoading={chartsLoading || !dashboardInfo?.id} showPropertiesModal={this.showPropertiesModal} manageEmbedded={this.showEmbedModal} refreshLimit={refreshLimit} diff --git a/superset-frontend/src/dashboard/components/PublishedStatus/index.jsx b/superset-frontend/src/dashboard/components/PublishedStatus/index.jsx index c966542146..abc9671fbc 100644 --- a/superset-frontend/src/dashboard/components/PublishedStatus/index.jsx +++ b/superset-frontend/src/dashboard/components/PublishedStatus/index.jsx @@ -28,6 +28,7 @@ const propTypes = { savePublished: PropTypes.func.isRequired, canEdit: PropTypes.bool, canSave: PropTypes.bool, + loading: PropTypes.bool, }; const draftButtonTooltip = t( @@ -54,6 +55,9 @@ export default class PublishedStatus extends Component { } render() { + if (this.props.loading) { + return <Label>{t('...')}</Label>; + } // Show everybody the draft badge if (!this.props.isPublished) { // if they can edit the dash, make the badge a button diff --git a/superset-frontend/src/dashboard/components/RefreshIntervalModal.tsx b/superset-frontend/src/dashboard/components/RefreshIntervalModal.tsx index 89f34bc33d..b1fcee9c54 100644 --- a/superset-frontend/src/dashboard/components/RefreshIntervalModal.tsx +++ b/superset-frontend/src/dashboard/components/RefreshIntervalModal.tsx @@ -50,16 +50,16 @@ const InnerStyledDiv = styled.div` type RefreshIntervalModalProps = { addSuccessToast: (msg: string) => void; triggerNode: JSX.Element; - refreshFrequency: number; + refreshFrequency?: number; onChange: (refreshLimit: number, editMode: boolean) => void; editMode: boolean; refreshLimit?: number; refreshWarning: string | null; - refreshIntervalOptions: [number, string][]; + refreshIntervalOptions?: [number, string][]; }; type RefreshIntervalModalState = { - refreshFrequency: number; + refreshFrequency?: number; custom_hour: number; custom_min: number; custom_sec: number; @@ -93,9 +93,11 @@ class RefreshIntervalModal extends PureComponent< } onSave() { - this.props.onChange(this.state.refreshFrequency, this.props.editMode); - this.modalRef?.current?.close(); - this.props.addSuccessToast(t('Refresh interval saved')); + if (this.state.refreshFrequency !== undefined) { + this.props.onChange(this.state.refreshFrequency, this.props.editMode); + this.modalRef?.current?.close(); + this.props.addSuccessToast(t('Refresh interval saved')); + } } onCancel() { @@ -108,7 +110,7 @@ class RefreshIntervalModal extends PureComponent< handleFrequencyChange(value: number) { const { refreshIntervalOptions } = this.props; this.setState({ - refreshFrequency: value || refreshIntervalOptions[0][0], + refreshFrequency: value || refreshIntervalOptions?.[0]?.[0], }); this.setState({ @@ -135,7 +137,7 @@ class RefreshIntervalModal extends PureComponent< refresh_options.push({ value: -1, label: t('Custom interval') }); refresh_options.push( - ...refreshIntervalOptions.map(option => ({ + ...refreshIntervalOptions?.map(option => ({ value: option[0], label: t(option[1]), })), @@ -221,7 +223,11 @@ class RefreshIntervalModal extends PureComponent< </FormLabel> <Select ariaLabel={t('Refresh interval')} - options={this.createIntervalOptions(refreshIntervalOptions)} + options={ + refreshIntervalOptions + ? this.createIntervalOptions(refreshIntervalOptions) + : [] + } value={refreshFrequency} onChange={this.handleFrequencyChange} sortComparator={propertyComparator('value')} @@ -307,6 +313,7 @@ class RefreshIntervalModal extends PureComponent< <Button buttonStyle="primary" buttonSize="small" + disabled={!refreshIntervalOptions} onClick={() => this.refresh_custom_val( custom_block,
