Tab names can now be updated from the entity explorer (#2779)

This commit is contained in:
Abhinav Jha 2021-02-01 18:47:55 +05:30 committed by GitHub
parent 344b7f73a6
commit 51696b7220
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 134 additions and 42 deletions

View File

@ -14,6 +14,7 @@ import { removeSpecialChars } from "utils/helpers";
import { AppState } from "reducers";
import { Page, ReduxActionTypes } from "constants/ReduxActionConstants";
import { Colors } from "constants/Colors";
import { WidgetTypes } from "constants/WidgetConstants";
const searchHighlightSpanClassName = "token";
const searchTokenizationDelimiter = "!!";
@ -71,6 +72,24 @@ export interface EntityNameProps {
export const EntityName = forwardRef(
(props: EntityNameProps, ref: React.Ref<HTMLDivElement>) => {
const { name, updateEntityName, searchKeyword } = props;
const tabs:
| Array<{ id: string; widgetId: string; label: string }>
| undefined = useSelector((state: AppState) => {
if (state.entities.canvasWidgets.hasOwnProperty(props.entityId)) {
const widget = state.entities.canvasWidgets[props.entityId];
if (
widget.parentId &&
state.entities.canvasWidgets.hasOwnProperty(widget.parentId)
) {
const parent = state.entities.canvasWidgets[widget.parentId];
if (parent.type === WidgetTypes.TABS_WIDGET) {
return parent.tabs;
}
}
}
return;
});
const nameUpdateError = useSelector((state: AppState) => {
return state.ui.explorer.updateEntityError === props.entityId;
});
@ -99,12 +118,20 @@ export const EntityName = forwardRef(
);
const hasNameConflict = useCallback(
(newName: string) =>
!(
existingPageNames.indexOf(newName) === -1 &&
existingActionNames.indexOf(newName) === -1 &&
existingWidgetNames.indexOf(newName) === -1
),
(
newName: string,
tabs?: Array<{ id: string; widgetId: string; label: string }>,
) => {
if (tabs === undefined) {
return !(
existingPageNames.indexOf(newName) === -1 &&
existingActionNames.indexOf(newName) === -1 &&
existingWidgetNames.indexOf(newName) === -1
);
} else {
return tabs.findIndex((tab) => tab.label === newName) > -1;
}
},
[existingPageNames, existingActionNames, existingWidgetNames],
);
@ -112,7 +139,7 @@ export const EntityName = forwardRef(
(newName: string): string | boolean => {
if (!newName || newName.trim().length === 0) {
return "Please enter a name";
} else if (newName !== name && hasNameConflict(newName)) {
} else if (newName !== name && hasNameConflict(newName, tabs)) {
return `${newName} is already being used.`;
}
return false;

View File

@ -19,6 +19,7 @@ import {
initCanvasLayout,
updateCurrentPage,
updateWidgetNameSuccess,
updateAndSaveLayout,
} from "actions/pageActions";
import PageApi, {
ClonePageRequest,
@ -75,6 +76,7 @@ import { getQueryParams } from "utils/AppsmithUtils";
import PerformanceTracker, {
PerformanceTransactionName,
} from "utils/PerformanceTracker";
import { WidgetTypes } from "constants/WidgetConstants";
const getWidgetName = (state: AppState, widgetId: string) =>
state.entities.canvasWidgets[widgetId];
@ -544,47 +546,110 @@ export function* updateWidgetNameSaga(
const pageId = yield select(getCurrentPageId);
const existingPageNames = yield select(getExistingPageNames);
// check if name is not conflicting with any
// existing entity/api/queries/reserved words
if (
isNameValid(action.payload.newName, {
...evalTree,
...existingPageNames,
})
) {
const request: UpdateWidgetNameRequest = {
newName: action.payload.newName,
oldName: widgetName,
pageId,
layoutId,
};
const response: UpdateWidgetNameResponse = yield call(
PageApi.updateWidgetName,
request,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield updateCanvasWithDSL(response.data, pageId, layoutId);
// TODO(abhinav): Why do we need to jump through these hoops just to
// change the tab name? Figure out a better design to make this moot.
const tabs:
| Array<{
id: string;
widgetId: string;
label: string;
}>
| undefined = yield select((state: AppState) => {
// Check if this widget exists in the canvas widgets
if (state.entities.canvasWidgets.hasOwnProperty(action.payload.id)) {
// If it does assign it to a variable
const widget = state.entities.canvasWidgets[action.payload.id];
// Check if this widget has a parent in the canvas widgets
if (
widget.parentId &&
state.entities.canvasWidgets.hasOwnProperty(widget.parentId)
) {
// If the parent exists assign it to a variable
const parent = state.entities.canvasWidgets[widget.parentId];
// Check if this parent is a TABS_WIDGET
if (parent.type === WidgetTypes.TABS_WIDGET) {
// If it is return the tabs property
return parent.tabs;
}
}
}
// This isn't a tab in a tabs widget so return undefined
return;
});
yield put(updateWidgetNameSuccess());
// Add this to the page DSLs for entity explorer
// If we're trying to update the name of a tab in the TABS_WIDGET
if (tabs !== undefined) {
// Get all canvas widgets
const stateWidgets = yield select(getWidgets);
// Shallow copy canvas widgets as they're immutable
const widgets = { ...stateWidgets };
// Get the parent Id of the tab (canvas widget) whose name we're updating
const parentId = widgets[action.payload.id].parentId;
// Update the tabName property of the tab (canvas widget)
widgets[action.payload.id] = {
...widgets[action.payload.id],
tabName: action.payload.newName,
};
// Shallow copy the parent widget so that we can update the properties
const parent = { ...widgets[parentId] };
// Update the tabs property of the parent tabs widget
parent.tabs = tabs.map(
(tab: { widgetId: string; label: string; id: string }) => {
if (tab.widgetId === action.payload.id) {
return { ...tab, label: action.payload.newName };
}
return tab;
},
);
// replace the parent widget in the canvas widgets
widgets[parentId] = parent;
// Update and save the new widgets
yield put(updateAndSaveLayout(widgets));
// Send a update saying that we've successfully updated the name
yield put(updateWidgetNameSuccess());
} else {
// check if name is not conflicting with any
// existing entity/api/queries/reserved words
if (
isNameValid(action.payload.newName, {
...evalTree,
...existingPageNames,
})
) {
const request: UpdateWidgetNameRequest = {
newName: action.payload.newName,
oldName: widgetName,
pageId,
layoutId,
};
const response: UpdateWidgetNameResponse = yield call(
PageApi.updateWidgetName,
request,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield updateCanvasWithDSL(response.data, pageId, layoutId);
yield put(updateWidgetNameSuccess());
// Add this to the page DSLs for entity explorer
yield put({
type: ReduxActionTypes.FETCH_PAGE_DSL_SUCCESS,
payload: {
pageId: pageId,
dsl: response.data.dsl,
},
});
}
} else {
yield put({
type: ReduxActionTypes.FETCH_PAGE_DSL_SUCCESS,
type: ReduxActionErrorTypes.UPDATE_WIDGET_NAME_ERROR,
payload: {
pageId: pageId,
dsl: response.data.dsl,
error: {
message: `Entity name: ${action.payload.newName} is already being used.`,
},
},
});
}
} else {
yield put({
type: ReduxActionErrorTypes.UPDATE_WIDGET_NAME_ERROR,
payload: {
error: {
message: `Entity name: ${action.payload.newName} is already being used.`,
},
},
});
}
} catch (error) {
yield put({