Skip to content

Commit

Permalink
fix(frontend): update header back button logic (#3199)
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgeepc committed Oct 2, 2023
1 parent 373afc2 commit a352ac1
Show file tree
Hide file tree
Showing 10 changed files with 76 additions and 18 deletions.
7 changes: 3 additions & 4 deletions web/src/components/ResourceCard/ResourceCard.styled.ts
@@ -1,8 +1,9 @@
import {MoreOutlined} from '@ant-design/icons';
import {Button, Space, Typography} from 'antd';
import styled from 'styled-components';

import emptyStateIcon from 'assets/SpanAssertionsEmptyState.svg';
import styled from 'styled-components';
import Link from 'components/Link';
import {ResourceType} from 'types/Resource.type';

export const ActionButton = styled(MoreOutlined)`
Expand Down Expand Up @@ -77,9 +78,7 @@ export const HeaderDetail = styled(Typography.Text)`
margin-right: 8px;
`;

export const Link = styled(Button).attrs({
type: 'link',
})`
export const CustomLink = styled(Link)`
color: ${({theme}) => theme.color.primary};
font-weight: 600;
padding: 0;
Expand Down
14 changes: 11 additions & 3 deletions web/src/components/ResourceCard/ResourceCardRuns.tsx
Expand Up @@ -11,7 +11,15 @@ interface IProps {
resourcePath: string;
}

const ResourceCardRuns = ({children, hasMoreRuns, hasRuns, isCollapsed, isLoading, onViewAll, resourcePath}: IProps) => {
const ResourceCardRuns = ({
children,
hasMoreRuns,
hasRuns,
isCollapsed,
isLoading,
onViewAll,
resourcePath,
}: IProps) => {
if (isCollapsed) return null;

return (
Expand All @@ -28,9 +36,9 @@ const ResourceCardRuns = ({children, hasMoreRuns, hasRuns, isCollapsed, isLoadin

{hasMoreRuns && (
<S.FooterContainer>
<S.Link data-cy="test-details-link" onClick={onViewAll} href={resourcePath}>
<S.CustomLink data-cy="test-details-link" onClick={onViewAll} to={resourcePath}>
View all runs
</S.Link>
</S.CustomLink>
</S.FooterContainer>
)}

Expand Down
5 changes: 3 additions & 2 deletions web/src/components/RunDetailLayout/HeaderLeft.tsx
Expand Up @@ -9,9 +9,10 @@ import * as S from './RunDetailLayout.styled';
interface IProps {
name: string;
triggerType: string;
origin: string;
}

const HeaderLeft = ({name, triggerType}: IProps) => {
const HeaderLeft = ({name, triggerType, origin}: IProps) => {
const {run: {createdAt, testSuiteId, testSuiteRunId, executionTime, trace, traceId, testVersion} = {}, run} =
useTestRun();
const createdTimeAgo = Date.getTimeAgo(createdAt ?? '');
Expand All @@ -36,7 +37,7 @@ const HeaderLeft = ({name, triggerType}: IProps) => {

return (
<S.Section $justifyContent="flex-start">
<a data-cy="test-header-back-button" onClick={() => navigate(-1)}>
<a data-cy="test-header-back-button" onClick={() => navigate(origin)}>
<S.BackIcon />
</a>
<S.InfoContainer>
Expand Down
7 changes: 5 additions & 2 deletions web/src/components/RunDetailLayout/RunDetailLayout.tsx
Expand Up @@ -12,6 +12,8 @@ import {isRunStateSucceeded} from 'models/TestRun.model';
import {useNotification} from 'providers/Notification/Notification.provider';
import {useSettingsValues} from 'providers/SettingsValues/SettingsValues.provider';
import {useTestRun} from 'providers/TestRun/TestRun.provider';
import {useAppSelector} from 'redux/hooks';
import UserSelectors from 'selectors/User.selectors';
import TestRunAnalyticsService from 'services/Analytics/TestRunAnalytics.service';
import {ConfigMode} from 'types/DataStore.types';
import HeaderLeft from './HeaderLeft';
Expand Down Expand Up @@ -41,6 +43,7 @@ const RunDetailLayout = ({test: {id, name, trigger}, test}: IProps) => {
const {dataStoreConfig} = useSettingsValues();
const [prevState, setPrevState] = useState(run.state);
useDocumentTitle(`${name} - ${run.state}`);
const runOriginPath = useAppSelector(UserSelectors.selectRunOriginPath);

useEffect(() => {
const isNoTracingMode = dataStoreConfig.mode === ConfigMode.NO_TRACING_MODE;
Expand All @@ -59,10 +62,10 @@ const RunDetailLayout = ({test: {id, name, trigger}, test}: IProps) => {

const tabBarExtraContent = useMemo(
() => ({
left: <HeaderLeft name={name} triggerType={trigger.type.toUpperCase()} />,
left: <HeaderLeft name={name} triggerType={trigger.type.toUpperCase()} origin={runOriginPath} />,
right: <HeaderRight testId={id} />,
}),
[id, name, trigger.type]
[id, name, trigger.type, runOriginPath]
);

return (
Expand Down
5 changes: 4 additions & 1 deletion web/src/components/TestSuiteHeader/TestSuiteHeader.tsx
Expand Up @@ -7,6 +7,8 @@ import {TestState as TestStateEnum} from 'constants/TestRun.constants';
import {useDashboard} from 'providers/Dashboard/Dashboard.provider';
import {useTestSuite} from 'providers/TestSuite/TestSuite.provider';
import {useTestSuiteRun} from 'providers/TestSuiteRun/TestSuite.provider';
import {useAppSelector} from 'redux/hooks';
import UserSelectors from 'selectors/User.selectors';
import * as S from './TestSuiteHeader.styled';
import VariableSetSelector from '../VariableSetSelector/VariableSetSelector';

Expand Down Expand Up @@ -34,11 +36,12 @@ const TestSuiteHeader = () => {
const {id: testSuiteId, name, version, description} = testSuite;
const {state, id: runId, allStepsRequiredGatesPassed} = run;
const lastPath = getLastPath(pathname);
const runOriginPath = useAppSelector(UserSelectors.selectRunOriginPath);

return (
<S.Container>
<S.Section>
<a onClick={() => navigate('/')} data-cy="testsuite-header-back-button">
<a onClick={() => navigate(runOriginPath)} data-cy="testsuite-header-back-button">
<S.BackIcon />
</a>
<div>
Expand Down
4 changes: 4 additions & 0 deletions web/src/hooks/useRouterSync.ts
Expand Up @@ -8,6 +8,10 @@ const useRouterSync = () => {
useEffect(() => {
return RouterMiddleware.startListening({testId: params.testId, runId: params.runId});
}, [params.runId, params.testId]);

useEffect(() => {
return RouterMiddleware.startListeningForLocationChange();
}, []);
};

export default useRouterSync;
38 changes: 35 additions & 3 deletions web/src/redux/Router.middleware.ts
@@ -1,23 +1,30 @@
import {createListenerMiddleware} from '@reduxjs/toolkit';
import type {TypedStartListening} from '@reduxjs/toolkit';
import {LOCATION_CHANGE} from 'redux-first-history';
import {parse} from 'query-string';
import RouterActions from './actions/Router.actions';
import {RootState} from './store';
import {runOriginPathAdded} from './slices/User.slice';
import {AppDispatch, RootState} from './store';

type AppStartListening = TypedStartListening<RootState, AppDispatch>;
const listener = createListenerMiddleware();
const startAppListening = listener.startListening as AppStartListening;

const sideEffectActionList = [RouterActions.updateSelectedAssertion, RouterActions.updateSelectedSpan];

const runUrlRegex = /^\/(test|testsuite)\/([^\/]+)\/run\/([^\/]+)(.*)$/;

const RouterMiddleware = () => ({
middleware: listener.middleware,

startListening(params = {}) {
return listener.startListening({
return startAppListening({
predicate: ({type = ''}) =>
type === LOCATION_CHANGE || (type.endsWith('/fulfilled') && !type.startsWith('router/')),
effect(_, {dispatch, getState}) {
const {
router: {location},
} = getState() as RootState;
} = getState();

const search = parse(location?.search || '');

Expand All @@ -27,6 +34,31 @@ const RouterMiddleware = () => ({
},
});
},

startListeningForLocationChange() {
return startAppListening({
predicate: action => {
const pathname = action?.payload?.location?.pathname ?? '';
return action?.type === LOCATION_CHANGE && pathname.match(runUrlRegex);
},
effect: async (_, {dispatch, getOriginalState, getState}) => {
const {
router: {location: prevLocation},
} = getOriginalState();

const {
router: {location: currLocation},
} = getState();

const prevPathname = prevLocation?.pathname ?? '';

if (!prevPathname.match(runUrlRegex)) {
const defaultPath = currLocation?.pathname?.includes('testsuite') ? '/testsuites' : '/';
dispatch(runOriginPathAdded(prevLocation?.pathname ?? defaultPath));
}
},
});
},
});

export default RouterMiddleware();
11 changes: 8 additions & 3 deletions web/src/redux/slices/User.slice.ts
@@ -1,9 +1,10 @@
import {createAction, createSlice} from '@reduxjs/toolkit';
import {PayloadAction, createAction, createSlice} from '@reduxjs/toolkit';
import UserPreferencesService from 'services/UserPreferences.service';
import {IUserState, TUserPreferenceKey, TUserPreferenceValue} from 'types/User.types';

export const initialState: IUserState = {
preferences: UserPreferencesService.get(),
runOriginPath: '/',
};

interface ISetUserPreferencesProps {
Expand All @@ -20,13 +21,17 @@ export const setUserPreference = createAction('user/setUserPreference', ({key, v
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {},
reducers: {
runOriginPathAdded(state, {payload}: PayloadAction<string>) {
state.runOriginPath = payload;
},
},
extraReducers: builder => {
builder.addCase(setUserPreference, (state, {payload: {preferences}}) => {
state.preferences = preferences;
});
},
});

// export const {} = testDefinitionSlice.actions;
export const {runOriginPathAdded} = userSlice.actions;
export default userSlice.reducer;
2 changes: 2 additions & 0 deletions web/src/selectors/User.selectors.ts
Expand Up @@ -13,6 +13,8 @@ const UserSelectors = () => ({
return user.preferences[key];
}
),

selectRunOriginPath: (state: RootState) => state.user.runOriginPath,
});

export default UserSelectors();
1 change: 1 addition & 0 deletions web/src/types/User.types.ts
Expand Up @@ -12,4 +12,5 @@ export type TUserPreferenceValue<K extends TUserPreferenceKey = TUserPreferenceK

export interface IUserState {
preferences: IUserPreferences;
runOriginPath: string;
}

0 comments on commit a352ac1

Please sign in to comment.