Skip to content

Commit

Permalink
Merge pull request wso2#2164 from IsankaSR/update-url
Browse files Browse the repository at this point in the history
Update the URL with a fragment when tabs change in the application edit section
  • Loading branch information
brionmario authored Jun 21, 2021
2 parents 9216dca + 80f70ae commit f860554
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 110 deletions.
149 changes: 113 additions & 36 deletions apps/console/src/features/applications/components/edit-application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import { AlertLevels, SBACInterface, TestableComponentInterface } from "@wso2is/
import { addAlert } from "@wso2is/core/store";
import { ConfirmationModal, ContentLoader, CopyInputField, ResourceTab } from "@wso2is/react-components";
import Axios from "axios";
import inRange from "lodash-es/inRange";
import isEmpty from "lodash-es/isEmpty";
import React, { FunctionComponent, ReactElement, useEffect, useState } from "react";
import React, { FunctionComponent, ReactElement, SyntheticEvent, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
import { Form, Grid } from "semantic-ui-react";
Expand All @@ -37,7 +38,7 @@ import {
} from "./settings";
import { Info } from "./settings/info";
import { ComponentExtensionPlaceholder, applicationConfig } from "../../../extensions";
import { AppState, CORSOriginsListInterface, FeatureConfigInterface, getCORSOrigins } from "../../core";
import { AppState, CORSOriginsListInterface, FeatureConfigInterface, getCORSOrigins, history } from "../../core";
import { getInboundProtocolConfig } from "../api";
import { ApplicationManagementConstants } from "../constants";
import {
Expand All @@ -47,11 +48,10 @@ import {
OIDCApplicationConfigurationInterface,
OIDCDataInterface,
SAMLApplicationConfigurationInterface,
SupportedAuthProtocolTypes
SupportedAuthProtocolTypes,
URLFragmentTypes
} from "../models";
import { ApplicationManagementUtils } from "../utils";
import SAMLApplicationTemplate
from "../data/application-templates/templates/saml-web-application/saml-web-application.json";
import CustomApplicationTemplate
from "../data/application-templates/templates/custom-application/custom-application.json";

Expand All @@ -63,10 +63,6 @@ interface EditApplicationPropsInterface extends SBACInterface<FeatureConfigInter
* Editing application.
*/
application: ApplicationInterface;
/**
* Default active tab index.
*/
defaultActiveIndex?: number;
/**
* Used to the configured inbound protocols list from the parent component.
*/
Expand All @@ -91,10 +87,6 @@ interface EditApplicationPropsInterface extends SBACInterface<FeatureConfigInter
* Application template.
*/
template?: ApplicationTemplateInterface;
/**
* Callback to see if tab extensions are available
*/
isTabExtensionsAvailable: (isAvailable: boolean) => void;
/**
* Make the form read only.
*/
Expand All @@ -118,15 +110,13 @@ export const EditApplication: FunctionComponent<EditApplicationPropsInterface> =

const {
application,
defaultActiveIndex,
featureConfig,
isLoading,
getConfiguredInboundProtocolsList,
getConfiguredInboundProtocolConfigs,
onDelete,
onUpdate,
template,
isTabExtensionsAvailable,
readOnly,
urlSearchParams,
[ "data-testid" ]: testId
Expand Down Expand Up @@ -161,6 +151,8 @@ export const EditApplication: FunctionComponent<EditApplicationPropsInterface> =
const [ isOIDCConfigsLoading, setOIDCConfigsLoading ] = useState<boolean>(false);
const [ isSAMLConfigsLoading, setSAMLConfigsLoading ] = useState<boolean>(false);
const [ activeTabIndex, setActiveTabIndex ] = useState<number>(undefined);
const [ defaultActiveIndex, setDefaultActiveIndex ] = useState<number>(undefined);
const [ totalTabs, setTotalTabs ] = useState<number>(undefined);

/**
* Called when an application updates.
Expand All @@ -173,21 +165,74 @@ export const EditApplication: FunctionComponent<EditApplicationPropsInterface> =
};

/**
* Called when the defaultActiveIndex updates.
* Set the defaultTabIndex when the application template updates.
*/
useEffect(() => {

if(!template) {
return;
}

let defaultTabIndex: number = 0;

if(applicationConfig.editApplication.extendTabs) {
defaultTabIndex=1;
}

setDefaultActiveIndex(defaultTabIndex);

if(isEmpty(window.location.hash)){

if(urlSearchParams.get(ApplicationManagementConstants.APP_STATE_STRONG_AUTH_PARAM_KEY) !==
ApplicationManagementConstants.APP_STATE_STRONG_AUTH_PARAM_VALUE) {

handleDefaultTabIndexChange(defaultTabIndex);

return;
}

// When application selection is done through the strong authentication flow.
if(template.id === CustomApplicationTemplate.id) {
handleActiveTabIndexChange(3);
} else {
handleActiveTabIndexChange(4);
}
}
},[template]);

/**
* Called when the URL fragment updates.
*/
useEffect( () => {
// Change defaultActiveIndex value for custom applications.
if ((application?.templateId === CustomApplicationTemplate.id
|| application?.templateId === ApplicationManagementConstants.CUSTOM_APPLICATION_OIDC
|| application?.templateId === ApplicationManagementConstants.CUSTOM_APPLICATION_PASSIVE_STS
|| application?.templateId === ApplicationManagementConstants.CUSTOM_APPLICATION_SAML)
&& !urlSearchParams.get(ApplicationManagementConstants.APP_STATE_PROTOCOL_PARAM_KEY)) {
setActiveTabIndex(defaultActiveIndex - 1);

if(totalTabs === undefined || window.location.hash.includes(URLFragmentTypes.VIEW) ||
isEmpty(window.location.hash)) {

return;
}
setActiveTabIndex(defaultActiveIndex);

},[defaultActiveIndex]);
const urlFragment: string[] = window.location.hash.split("#"+URLFragmentTypes.TAB_INDEX);

if(urlFragment.length === 2 && isEmpty(urlFragment[0]) && /^\d+$/.test(urlFragment[1])) {

const tabIndex: number = parseInt(urlFragment[1], 10);

if(inRange(tabIndex, 0, totalTabs)) {
if(tabIndex === activeTabIndex) {
return;
}
handleActiveTabIndexChange(tabIndex);

} else {
// Change the tab index to defaultActiveIndex for invalid URL fragments.
handleDefaultTabIndexChange(defaultActiveIndex);
}

} else {
// Change the tab index to defaultActiveIndex for invalid URL fragments.
handleDefaultTabIndexChange(defaultActiveIndex);
}
}, [ window.location.hash, totalTabs ]);

/**
* Fetch the allowed origins list whenever there's an update.
Expand Down Expand Up @@ -309,10 +354,6 @@ export const EditApplication: FunctionComponent<EditApplicationPropsInterface> =
type: "tab"
});

if (Array.isArray(extensions) && extensions.length > 0) {
isTabExtensionsAvailable(true);
}

setTabPaneExtensions(extensions);
setIsApplicationUpdated(false);
}, [
Expand All @@ -333,6 +374,46 @@ export const EditApplication: FunctionComponent<EditApplicationPropsInterface> =
setShowClientSecretHashDisclaimerModal(true);
}, [ urlSearchParams.get(ApplicationManagementConstants.CLIENT_SECRET_HASH_ENABLED_URL_SEARCH_PARAM_KEY) ]);

/**
* Handles the defaultActiveIndex change.
*/
const handleDefaultTabIndexChange = (defaultActiveIndex: number): void => {

if (template.id === CustomApplicationTemplate.id && defaultActiveIndex > 0) {
handleActiveTabIndexChange(defaultActiveIndex - 1);

return;
}

handleActiveTabIndexChange(defaultActiveIndex);
};

/**
* Handles the activeTabIndex change.
*
* @param {number} tabIndex - Active tab index.
*/
const handleActiveTabIndexChange = (tabIndex:number): void => {

history.push({
hash: `#${ URLFragmentTypes.TAB_INDEX }${ tabIndex }`,
pathname: window.location.pathname
});

setActiveTabIndex(tabIndex);
};

/**
* Handles the tab change.
*
* @param {React.SyntheticEvent} e - Click event.
* @param {number} activeIndex - Active tab index.
*/
const handleTabChange = (e: SyntheticEvent, { activeIndex }): void => {

handleActiveTabIndexChange(activeIndex);
};

/**
* Todo Remove this mapping and fix the backend.
*/
Expand Down Expand Up @@ -727,13 +808,6 @@ export const EditApplication: FunctionComponent<EditApplicationPropsInterface> =
];
};

/**
* Handles the tab change.
*/
const handleTabChange = (e, { activeIndex }): void => {
setActiveTabIndex(activeIndex);
};

/**
* Renders the client secret hash disclaimer modal.
* @return {React.ReactElement}
Expand Down Expand Up @@ -862,6 +936,9 @@ export const EditApplication: FunctionComponent<EditApplicationPropsInterface> =
defaultActiveIndex={ defaultActiveIndex }
onTabChange={ handleTabChange }
panes= { resolveTabPanes() }
onInitialize={ ({ panesLength }) => {
setTotalTabs(panesLength);
} }
/>
{ showClientSecretHashDisclaimerModal && renderClientSecretHashDisclaimerModal() }
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import {
MainApplicationInterface,
SupportedAuthProtocolMetaTypes,
SupportedAuthProtocolTypes,
URLFragmentTypes,
emptyApplication
} from "../../models";
import { setAuthProtocolMeta } from "../../store";
Expand Down Expand Up @@ -234,7 +235,14 @@ export const ApplicationCreateWizard: FunctionComponent<ApplicationCreateWizardP
const location = response.headers.location;
const createdAppID = location.substring(location.lastIndexOf("/") + 1);

let defaultTabIndex: number = 0;

if (selectedTemplate.id === CustomApplicationTemplate.id) {
defaultTabIndex = 1;
}

history.push({
hash: `#${ URLFragmentTypes.TAB_INDEX }${ defaultTabIndex }`,
pathname: AppConstants.getPaths().get("APPLICATION_EDIT").replace(":id", createdAppID),
search: `?${ ApplicationManagementConstants.APP_STATE_URL_SEARCH_PARAM_KEY }=${
ApplicationManagementConstants.APP_STATE_URL_SEARCH_PARAM_VALUE }`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
ApplicationManagementUtils,
ApplicationTemplateLoadingStrategies,
SAMLConfigModes,
URLFragmentTypes,
getApplicationList
} from "../..";
import { applicationConfig } from "../../../../extensions";
Expand Down Expand Up @@ -246,25 +247,23 @@ export const MinimalAppCreateWizard: FunctionComponent<MinimalApplicationCreateW
const location = response.headers.location;
const createdAppID = location.substring(location.lastIndexOf("/") + 1);

let searchParams: string = `?${
ApplicationManagementConstants.APP_STATE_URL_SEARCH_PARAM_KEY }=${
ApplicationManagementConstants.APP_STATE_URL_SEARCH_PARAM_VALUE
}`;
let searchParams: string = "?";
let defaultTabIndex: number = 0;

if (isClientSecretHashEnabled) {
searchParams = `${ searchParams }&${
ApplicationManagementConstants.CLIENT_SECRET_HASH_ENABLED_URL_SEARCH_PARAM_KEY }=true`;
}

if (selectedTemplate.id === CustomApplicationTemplate.id) {
searchParams = `${ searchParams }&${
ApplicationManagementConstants.APP_STATE_PROTOCOL_PARAM_KEY }=true`;
defaultTabIndex = 1;
}

history.push({
pathname: AppConstants.getPaths().get("APPLICATION_EDIT")
.replace(":id", createdAppID),
search: searchParams
search: searchParams,
hash: `#${ URLFragmentTypes.TAB_INDEX }${ defaultTabIndex }`
});

return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,6 @@ export class ApplicationManagementConstants {
*/
public static readonly APP_STATE_STRONG_AUTH_PARAM_KEY = "isSignOn";

/**
* Value for sign on authentication param for application state.
* @constant
* @type {string}
*/
public static readonly APP_STATE_PROTOCOL_PARAM_KEY = "isProtocol";

/**
* Key for the URL search param for application readonly state.
* @constant
Expand Down
11 changes: 11 additions & 0 deletions apps/console/src/features/applications/models/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -637,3 +637,14 @@ export enum LoginFlowTypes {
SECOND_FACTOR_TOTP = "SECOND_FACTOR_TOTP",
DEFAULT = "DEFAULT"
}

/**
* Enum for URL fragment types used in the edit application.
*
* @readonly
* @enum {string}
*/
export enum URLFragmentTypes {
TAB_INDEX = "tab=",
VIEW = "view=",
}
Loading

0 comments on commit f860554

Please sign in to comment.