Hide pages in publish mode (#3106)

This commit is contained in:
akash-codemonk 2021-02-24 19:17:37 +05:30 committed by GitHub
parent cccdd89b14
commit 7fa5e3b1af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 116 additions and 17 deletions

View File

@ -0,0 +1,21 @@
const pages = require("../../../../locators/Pages.json");
const pageOne = "MyPage1";
const pageTwo = "MyPage2";
describe("Hide page", function() {
it("Hide page", function() {
cy.Createpage(pageOne);
cy.Createpage(pageTwo);
cy.GlobalSearchEntity(pageOne);
cy.xpath(pages.popover)
.last()
.click({ force: true });
cy.get(pages.hidePage).click({ force: true });
cy.ClearSearch();
cy.PublishtheApp();
cy.get(".t--page-switch-tab").should("have.length", 2);
});
});

View File

@ -7,6 +7,7 @@ describe("Page Load tests", () => {
cy.get("div")
.contains("Pages")
.next()
.next()
.click();
cy.get("h2").contains("Drag and drop a widget here");
cy.addDsl(dsl);

View File

@ -18,5 +18,6 @@
"editName": ".single-select >div:contains('Edit Name')",
"clonePage": ".single-select >div:contains('Clone')",
"deletePage": ".single-select >div:contains('Delete')",
"hidePage": ".single-select >div:contains('Hide')",
"entityQuery": ".t--entity-name:contains('Queries')"
}

View File

@ -155,12 +155,13 @@ export const clonePageSuccess = (
};
};
export const updatePage = (id: string, name: string) => {
export const updatePage = (id: string, name: string, isHidden: boolean) => {
return {
type: ReduxActionTypes.UPDATE_PAGE_INIT,
payload: {
id,
name,
isHidden,
},
};
};

View File

@ -66,6 +66,7 @@ export interface CreatePageRequest {
export interface UpdatePageRequest {
id: string;
name: string;
isHidden?: boolean;
}
export interface CreatePageResponse extends ApiResponse {
@ -78,6 +79,7 @@ export interface FetchPageListResponse extends ApiResponse {
id: string;
name: string;
isDefault: boolean;
isHidden?: boolean;
layouts: Array<PageLayout>;
}>;
organizationId: string;

View File

@ -503,6 +503,7 @@ export interface Page {
pageId: string;
isDefault: boolean;
latest?: boolean;
isHidden?: boolean;
}
export interface ClonePageSuccessPayload {

View File

@ -27,7 +27,7 @@ import {
import { editorInitializer } from "utils/EditorUtils";
import * as Sentry from "@sentry/react";
import log from "loglevel";
import { getPageList } from "selectors/editorSelectors";
import { getViewModePageList } from "selectors/editorSelectors";
const SentryRoute = Sentry.withSentryRouting(Route);
@ -111,7 +111,7 @@ class AppViewer extends Component<
const mapStateToProps = (state: AppState) => ({
isInitialized: getIsInitialized(state),
pages: getPageList(state),
pages: getViewModePageList(state),
});
const mapDispatchToProps = (dispatch: any) => ({

View File

@ -21,7 +21,7 @@ import {
import { connect } from "react-redux";
import { AppState } from "reducers";
import { getEditorURL } from "selectors/appViewSelectors";
import { getPageList } from "selectors/editorSelectors";
import { getViewModePageList } from "selectors/editorSelectors";
import { FormDialogComponent } from "components/editorComponents/form/FormDialogComponent";
import AppInviteUsersForm from "pages/organization/AppInviteUsersForm";
import { getCurrentOrgId } from "selectors/organizationSelectors";
@ -135,7 +135,7 @@ type AppViewerHeaderProps = {
};
export const AppViewerHeader = (props: AppViewerHeaderProps) => {
const { currentApplicationDetails, pages, currentOrgId, currentUser } = props;
const { currentApplicationDetails, currentOrgId, currentUser, pages } = props;
const isExampleApp = currentApplicationDetails?.appIsExample;
const userPermissions = currentApplicationDetails?.userPermissions ?? [];
const permissionRequired = PERMISSION_TYPE.MANAGE_APPLICATION;
@ -249,7 +249,7 @@ export const AppViewerHeader = (props: AppViewerHeaderProps) => {
};
const mapStateToProps = (state: AppState): AppViewerHeaderProps => ({
pages: getPageList(state),
pages: getViewModePageList(state),
url: getEditorURL(state),
currentApplicationDetails: state.ui.applications.currentApplication,
currentOrgId: getCurrentOrgId(state),

View File

@ -29,7 +29,7 @@ const Wrapper = styled(EntityTogglesWrapper)`
const StyledEntity = styled(Entity)`
& > div {
grid-template-columns: 20px auto 1fr auto;
grid-template-columns: 20px auto 1fr auto auto;
}
`;

View File

@ -19,6 +19,7 @@ import useClick from "utils/hooks/useClick";
export enum EntityClassNames {
CONTEXT_MENU = "entity-context-menu",
RIGHT_ICON = "entity-right-icon",
ADD_BUTTON = "t--entity-add-btn",
NAME = "t--entity-name",
COLLAPSE_TOGGLE = "t--entity-collapse-toggle",
@ -44,7 +45,7 @@ export const EntityItem = styled.div<{
width: 100%;
display: inline-grid;
grid-template-columns: ${(props) =>
props.spaced ? "20px auto 1fr 30px" : "8px auto 1fr 30px"};
props.spaced ? "20px auto 1fr auto 30px" : "8px auto 1fr auto 30px"};
border-radius: 0;
color: ${(props) => (props.active ? Colors.WHITE : Colors.ALTO)};
cursor: pointer;
@ -71,6 +72,14 @@ export const EntityItem = styled.div<{
&&&&:hover .${EntityClassNames.CONTEXT_MENU} {
visibility: visible;
}
& .${EntityClassNames.RIGHT_ICON} {
visibility: hidden;
padding-right: ${(props) => props.theme.spaces[2]}px;
}
&:hover .${EntityClassNames.RIGHT_ICON} {
visibility: visible;
}
`;
const IconWrapper = styled.span`
@ -83,6 +92,7 @@ export type EntityProps = {
name: string;
children?: ReactNode;
icon: ReactNode;
rightIcon?: ReactNode;
disabled?: boolean;
action?: () => void;
active?: boolean;
@ -172,6 +182,9 @@ export const Entity = forwardRef(
updateEntityName={updateNameCallback}
searchKeyword={props.searchKeyword}
/>
<IconWrapper className={EntityClassNames.RIGHT_ICON}>
{props.rightIcon}
</IconWrapper>
<AddButton
onClick={props.onCreate}
className={`${EntityClassNames.ADD_BUTTON}`}

View File

@ -38,6 +38,10 @@ export const homePageIcon = (
<Icon icon="home" iconSize={ENTITY_ICON_SIZE} color={Colors.JUNGLE_GREEN} />
);
export const hiddenPageIcon = (
<Icon icon="eye-off" iconSize={ENTITY_ICON_SIZE} color={Colors.ALTO} />
);
const WidgetIcon = MenuIcons.WIDGETS_COLORED_ICON;
export const widgetIcon = (
<WidgetIcon width={ENTITY_ICON_SIZE} height={ENTITY_ICON_SIZE} keepColors />

View File

@ -1,4 +1,4 @@
import React, { useCallback } from "react";
import React, { ReactNode, useCallback } from "react";
import { useDispatch } from "react-redux";
import TreeDropdown, {
TreeDropdownOption,
@ -9,7 +9,15 @@ import { ReduxActionTypes } from "constants/ReduxActionConstants";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { ContextMenuPopoverModifiers } from "../helpers";
import { initExplorerEntityNameEdit } from "actions/explorerActions";
import { clonePageInit } from "actions/pageActions";
import { clonePageInit, updatePage } from "actions/pageActions";
import styled from "styled-components";
import { Icon } from "@blueprintjs/core";
const CustomLabel = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
`;
export const PageContextMenu = (props: {
pageId: string;
@ -17,6 +25,7 @@ export const PageContextMenu = (props: {
applicationId: string;
className?: string;
isDefaultPage: boolean;
isHidden: boolean;
}) => {
const dispatch = useDispatch();
@ -58,6 +67,11 @@ export const PageContextMenu = (props: {
props.pageId,
]);
const setHiddenField = useCallback(
() => dispatch(updatePage(props.pageId, props.name, !props.isHidden)),
[dispatch, props.pageId, props.name, props.isHidden],
);
const optionTree: TreeDropdownOption[] = [
{
value: "rename",
@ -69,6 +83,17 @@ export const PageContextMenu = (props: {
onSelect: clonePage,
label: "Clone",
},
{
value: "visibility",
onSelect: setHiddenField,
// Possibly support ReactNode in TreeOption
label: ((
<CustomLabel>
{props.isHidden ? "Show" : "Hide"}
<Icon icon={props.isHidden ? "eye-open" : "eye-off"} iconSize={14} />
</CustomLabel>
) as ReactNode) as string,
},
];
if (!props.isDefaultPage) {
optionTree.push({

View File

@ -1,4 +1,4 @@
import React, { memo, useCallback } from "react";
import React, { useCallback } from "react";
import { Page } from "constants/ReduxActionConstants";
import Entity, { EntityClassNames } from "../Entity";
import { useParams } from "react-router";
@ -10,7 +10,7 @@ import PageContextMenu from "./PageContextMenu";
import { useSelector } from "react-redux";
import { AppState } from "reducers";
import { DataTreeAction } from "entities/DataTree/dataTreeFactory";
import { homePageIcon, pageIcon } from "../ExplorerIcons";
import { hiddenPageIcon, homePageIcon, pageIcon } from "../ExplorerIcons";
import { getPluginGroups } from "../Actions/helpers";
import ExplorerWidgetGroup from "../Widgets/WidgetGroup";
import { resolveAsSpaceChar } from "utils/helpers";
@ -28,7 +28,8 @@ type ExplorerPageEntityProps = {
searchKeyword?: string;
showWidgetsSidebar: (pageId: string) => void;
};
export const ExplorerPageEntity = memo((props: ExplorerPageEntityProps) => {
export const ExplorerPageEntity = (props: ExplorerPageEntityProps) => {
const params = useParams<ExplorerURLParams>();
const currentPageId = useSelector((state: AppState) => {
@ -50,10 +51,12 @@ export const ExplorerPageEntity = memo((props: ExplorerPageEntityProps) => {
name={props.page.pageName}
className={EntityClassNames.CONTEXT_MENU}
isDefaultPage={props.page.isDefault}
isHidden={!!props.page.isHidden}
/>
);
const icon = props.page.isDefault ? homePageIcon : pageIcon;
const rightIcon = !!props.page.isHidden ? hiddenPageIcon : null;
const addWidgetsFn = useCallback(
() => props.showWidgetsSidebar(props.page.pageId),
@ -70,9 +73,12 @@ export const ExplorerPageEntity = memo((props: ExplorerPageEntityProps) => {
entityId={props.page.pageId}
active={isCurrentPage}
isDefaultExpanded={isCurrentPage || !!props.searchKeyword}
updateEntityName={updatePage}
updateEntityName={(id, name) =>
updatePage(id, name, !!props.page.isHidden)
}
contextMenu={contextMenu}
onNameEdit={resolveAsSpaceChar}
rightIcon={rightIcon}
searchKeyword={props.searchKeyword}
>
<ExplorerWidgetGroup
@ -93,7 +99,7 @@ export const ExplorerPageEntity = memo((props: ExplorerPageEntityProps) => {
)}
</Entity>
);
});
};
ExplorerPageEntity.displayName = "ExplorerPageEntity";
(ExplorerPageEntity as any).whyDidYouRender = {

View File

@ -93,12 +93,13 @@ export const pageListReducer = createReducer(initialState, {
}),
[ReduxActionTypes.UPDATE_PAGE_SUCCESS]: (
state: PageListReduxState,
action: ReduxAction<{ id: string; name: string }>,
action: ReduxAction<{ id: string; name: string; isHidden?: boolean }>,
) => {
const pages = [...state.pages];
const updatedPage = pages.find((page) => page.pageId === action.payload.id);
if (updatedPage) {
updatedPage.pageName = action.payload.name;
updatedPage.isHidden = !!action.payload.isHidden;
}
return { ...state, pages };
},

View File

@ -85,6 +85,7 @@ export function* publishApplicationSaga(
const applicationId = yield select(getCurrentApplicationId);
const currentPageId = yield select(getCurrentPageId);
let appicationViewPageUrl = getApplicationViewerPageURL(
applicationId,
currentPageId,
@ -99,8 +100,9 @@ export function* publishApplicationSaga(
if (!windowReference || windowReference.closed) {
windowReference = window.open(appicationViewPageUrl, "_blank");
} else {
windowReference.location.reload();
windowReference.focus();
windowReference.location.href =
windowReference.location.origin + appicationViewPageUrl;
}
}
} catch (error) {

View File

@ -103,6 +103,7 @@ export function* fetchPageListSaga(
pageName: page.name,
pageId: page.id,
isDefault: page.isDefault,
isHidden: !!page.isHidden,
}));
yield put({
type: ReduxActionTypes.SET_CURRENT_ORG_ID,

View File

@ -85,6 +85,26 @@ export const getCurrentPageId = (state: AppState) =>
export const getCurrentApplicationId = (state: AppState) =>
state.entities.pageList.applicationId;
export const getViewModePageList = createSelector(
getPageList,
getCurrentPageId,
(pageList: PageListReduxState["pages"], currentPageId?: string) => {
if (currentPageId) {
const currentPage = pageList.find(
(page) => page.pageId === currentPageId,
);
if (!!currentPage?.isHidden) {
return [currentPage];
}
const visiblePages = pageList.filter((page) => !page.isHidden);
return visiblePages;
}
return [];
},
);
export const getCurrentPageName = createSelector(
getPageListState,
(pageList: PageListReduxState) =>