Skip to content

Commit 56db810

Browse files
Merge branch 'qa' into PROD-1565_work-details
2 parents 9b4944d + 79e2bcc commit 56db810

File tree

17 files changed

+153
-91
lines changed

17 files changed

+153
-91
lines changed

src-ts/header/tool-selectors/tool-selectors-narrow/tool-selector-narrow/ToolSelectorNarrow.tsx

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import classNames from 'classnames'
2-
import { FC } from 'react'
2+
import { FC, useContext } from 'react'
33
import { Link, useLocation } from 'react-router-dom'
44

5-
import { IconOutline, PlatformRoute, routeIsActive } from '../../../../lib'
5+
import { IconOutline, PlatformRoute, routeContext, RouteContextData, routeIsActive } from '../../../../lib'
66

77
import styles from './ToolSelectorNarrow.module.scss'
88

@@ -14,22 +14,23 @@ const isParamRoute: (route: string) => boolean = (route: string) => !!route.matc
1414

1515
const ToolSelectorNarrow: FC<ToolSelectorNarrowProps> = (props: ToolSelectorNarrowProps) => {
1616

17-
const route: PlatformRoute = props.route
18-
const path: string = props.route.route
17+
const { getPathFromRoute }: RouteContextData = useContext(routeContext)
18+
const toolRoute: PlatformRoute = props.route
19+
const toolPath: string = getPathFromRoute(toolRoute)
1920

2021
const baseClass: string = 'tool-selector-narrow'
21-
const isActive: boolean = routeIsActive(useLocation().pathname, path)
22+
const isActive: boolean = routeIsActive(useLocation().pathname, toolPath)
2223
const activeIndicaterClass: string = `${baseClass}-${isActive ? '' : 'in'}active`
23-
const hasChildren: boolean = !!route.children.some(child => !!child.route && !isParamRoute(child.route))
24+
const hasChildren: boolean = !!toolRoute.children.some(child => !!child.route && !isParamRoute(child.route))
2425

2526
return (
2627
<div className={styles[baseClass]}>
2728
<Link
2829
className={classNames(styles[`${baseClass}-link`], styles[activeIndicaterClass])}
29-
key={path}
30-
to={path}
30+
key={toolPath}
31+
to={toolPath}
3132
>
32-
{route.title}
33+
{toolRoute.title}
3334
{hasChildren && <IconOutline.ChevronRightIcon />}
3435
</Link>
3536
</div>

src-ts/header/tool-selectors/tool-selectors-wide/tool-selector-wide/ToolSelectorWide.tsx

+16-9
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ import classNames from 'classnames'
22
import { FC, useContext } from 'react'
33
import { Link, useLocation } from 'react-router-dom'
44

5-
import { ToolTitle } from '../../../../config'
6-
import { PlatformRoute, routeContext, RouteContextData, routeIsActive, routeIsHome } from '../../../../lib'
5+
import {
6+
PlatformRoute,
7+
routeContext,
8+
RouteContextData,
9+
routeIsActive,
10+
routeIsHome,
11+
} from '../../../../lib'
712
import '../../../../lib/styles/index.scss'
813

914
import styles from './ToolSelectorWide.module.scss'
@@ -14,15 +19,17 @@ interface ToolSelectorWideProps {
1419

1520
const ToolSelectorWide: FC<ToolSelectorWideProps> = (props: ToolSelectorWideProps) => {
1621

17-
const { getPath, getPathFromRoute }: RouteContextData = useContext(routeContext)
18-
const currentPath: string = useLocation().pathname
22+
const { getPathFromRoute }: RouteContextData = useContext(routeContext)
23+
const activePath: string = useLocation().pathname
24+
const toolRoute: PlatformRoute = props.route
25+
const toolPath: string = getPathFromRoute(toolRoute)
26+
27+
const isActive: boolean = routeIsActive(activePath, toolPath)
1928

20-
// for now, the work tool should be active for all pages except the account
21-
const isActive: boolean = !routeIsActive(currentPath, getPath(ToolTitle.settings))
2229
const activeIndicatorClass: string = `tool-selector-wide-${isActive ? '' : 'in'}active`
2330

2431
// the tool link should be usable for all active routes except the home page
25-
const isLink: boolean = isActive && !routeIsHome(currentPath)
32+
const isLink: boolean = isActive && !routeIsHome(activePath)
2633

2734
return (
2835
<div className={classNames(
@@ -33,9 +40,9 @@ const ToolSelectorWide: FC<ToolSelectorWideProps> = (props: ToolSelectorWideProp
3340
<Link
3441
className='large-tab'
3542
tabIndex={-1}
36-
to={getPathFromRoute(props.route)}
43+
to={toolPath}
3744
>
38-
{props.route.title}
45+
{toolRoute.title}
3946
</Link>
4047
<div className={styles['active-indicator']}></div>
4148
</div>

src-ts/lib/functions/authentication-functions/authentication-url.config.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { EnvironmentConfig } from '../../../config'
2+
import { routeSelfServiceRoot } from '../../route-provider'
23

34
export const authentication: string = EnvironmentConfig.URL.ACCOUNTS_APP_CONNECTOR
45

56
export function login(fallback: string): string {
67
return `${authentication}?retUrl=${encodeURIComponent(window.location.href.match(/[^?]*/)?.[0] || fallback)}`
78
}
89

9-
export const logout: string = `${authentication}?logout=true&retUrl=${encodeURIComponent('https://' + window.location.host)}/self-service`
10+
export const logout: string = `${authentication}?logout=true&retUrl=${encodeURIComponent('https://' + window.location.host)}${routeSelfServiceRoot}`
1011

1112
export function signup(fallback: string): string {
1213
return `${login(fallback)}&regSource=tcBusiness&mode=signUp`

src-ts/lib/portal/Portal.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { createPortal } from 'react-dom'
33

44
interface PortalProps {
55
children: ReactNode
6+
className?: string,
67
portalId?: string
78
portalNode?: HTMLElement
89
portalRef?: MutableRefObject<HTMLElement>,
@@ -13,6 +14,7 @@ const Portal: FC<PortalProps> = (
1314
portalId,
1415
portalNode,
1516
children,
17+
className,
1618
portalRef,
1719
}: PortalProps) => {
1820

@@ -26,9 +28,12 @@ const Portal: FC<PortalProps> = (
2628
}
2729

2830
const backupHtmlNode: HTMLElement = document.createElement('div')
31+
if (className) {
32+
backupHtmlNode.classList.add(className)
33+
}
2934
document.body.appendChild(backupHtmlNode)
3035
return backupHtmlNode
31-
}, [portalNode]) as HTMLElement
36+
}, [portalNode, className]) as HTMLElement
3237

3338
useEffect(() => {
3439
return () => {
@@ -41,6 +46,7 @@ const Portal: FC<PortalProps> = (
4146
if (portalRef) {
4247
portalRef.current = portalNode ?? defaultPortalNode
4348
}
49+
4450
return createPortal(children, portalNode ?? defaultPortalNode)
4551
}
4652

+21-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
11
export const routeRoot: string = '/work/dashboard'
2+
export const routeSelfServiceRoot: string = '/self-service'
3+
export const routeSelfServiceStart: string = `${routeSelfServiceRoot}/wizard`
24

35
export function routeIsActive(activePath: string, pathName: string, rootPath?: string): boolean {
4-
return activePath?.startsWith(pathName)
5-
&& (pathName !== rootPath || activePath === rootPath)
6+
let isActive: boolean = isActivePath(activePath, pathName, rootPath)
7+
8+
// temporarilily, if the path we're testing against is the work tool
9+
// also check if the current path is self-service
10+
if (!isActive && pathName.startsWith(routeRoot)) {
11+
isActive = isActivePath(activePath, routeSelfServiceRoot)
12+
}
13+
14+
return isActive
615
}
716

817
export function routeIsHome(activePath: string): boolean {
918
// TODO: make the alternate home route configurable
10-
return [routeRoot, '/self-service'].some(route => activePath === route)
19+
return [routeRoot, routeSelfServiceRoot].some(route => activePath === route)
20+
}
21+
22+
export function routeWorkDetails(workId: string): string {
23+
return `${routeSelfServiceRoot}/work-items/${workId}`
24+
}
25+
26+
function isActivePath(activePath: string, pathName: string, rootPath?: string): boolean {
27+
return activePath?.startsWith(pathName)
28+
&& (pathName !== rootPath || activePath === rootPath)
1129
}

src-ts/lib/styles/variables/_palette.scss

+2-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ $blue-100: #2C95D7;
118118
$blue-75: #50ADE8;
119119
$blue-50: #83C5EE;
120120
$blue-25: #BAE1F9;
121-
$blue-15: #EAF6FD;
121+
$blue-15: #D6EDFC;
122+
$blue-10: #EAF6FD;
122123
// dark
123124
$blue-120: #2984BD;
124125
$blue-140: #16679A;

src-ts/lib/table/Table.tsx

+34-31
Original file line numberDiff line numberDiff line change
@@ -8,72 +8,75 @@ import { Tooltip } from '../tooltip'
88

99
import { TableCell } from './table-cell'
1010
import { TableColumn } from './table-column.model'
11-
import { tableGetSorted } from './table-functions'
11+
import { tableGetDefaultSort, tableGetSorted } from './table-functions'
1212
import { TableSort } from './table-sort'
1313
import styles from './Table.module.scss'
1414

1515
interface TableProps<T> {
1616
readonly columns: ReadonlyArray<TableColumn<T>>
1717
readonly data: ReadonlyArray<T>
18-
readonly defaultSort: Sort
1918
readonly onRowClick?: (data: T) => void
2019
}
2120

21+
interface DefaultSortDirectionMap {
22+
[columnProperty: string]: 'asc' | 'desc'
23+
}
24+
2225
const Table: <T extends { [propertyName: string]: any }>(props: TableProps<T>) => JSX.Element
2326
= <T extends { [propertyName: string]: any }>(props: TableProps<T>) => {
2427

25-
const [sort, setSort]: [Sort, Dispatch<SetStateAction<Sort>>] = useState<Sort>(props.defaultSort)
26-
const [sortMap, setSortMap]: [{ [fieldame: string]: boolean }, Dispatch<SetStateAction<{ [fieldame: string]: boolean }>>]
27-
= useState<{ [fieldame: string]: boolean }>({})
28+
const [sort, setSort]: [Sort | undefined, Dispatch<SetStateAction<Sort | undefined>>]
29+
= useState<Sort | undefined>(tableGetDefaultSort(props.columns))
30+
const [defaultSortDirectionMap, setDefaultSortDirectionMap]: [DefaultSortDirectionMap | undefined, Dispatch<SetStateAction<DefaultSortDirectionMap | undefined>>]
31+
= useState<DefaultSortDirectionMap | undefined>()
2832
const [sortedData, setSortData]: [ReadonlyArray<T>, Dispatch<SetStateAction<ReadonlyArray<T>>>]
2933
= useState<ReadonlyArray<T>>(props.data)
3034

3135
useEffect(() => {
3236

33-
// sort the data
34-
setSortData(tableGetSorted(props.data, sort, props.columns))
35-
36-
// create the sortmap to remember the last
37-
// sort direction of a column that is not
38-
// the currently sorted column
39-
const map: { [fieldame: string]: boolean } = {
40-
[sort.fieldName]: sort.direction === 'asc',
37+
if (!defaultSortDirectionMap) {
38+
const map: DefaultSortDirectionMap = {}
39+
props.columns
40+
.filter(col => !!col.propertyName)
41+
.forEach(col => map[col.propertyName as string] = col.defaultSortDirection || 'asc')
42+
setDefaultSortDirectionMap(map)
4143
}
4244

43-
// A) if column X is sorted in one direction,
44-
// then column Y is sorted,
45-
// then column X is sorted again,
46-
// the 2nd sort of column X will toggle its sort
47-
// direction
48-
// B) all columns default to sorting ascending, so if there
49-
// not a last known sort for a column, set it to descending
50-
// so the next toggle toggles it ascending.
51-
props.columns
52-
.filter(col => !!col.propertyName && col.propertyName !== sort.fieldName)
53-
.forEach(col => {
54-
const currentAscending: boolean | undefined = sortMap[col.propertyName as string]
55-
map[col.propertyName as string] = !!currentAscending
56-
})
57-
setSortMap(map)
45+
setSortData(tableGetSorted(props.data, props.columns, sort))
5846
},
5947
[
48+
defaultSortDirectionMap,
6049
sort,
6150
props.data,
6251
])
6352

6453
function toggleSort(fieldName: string): void {
54+
55+
// if we don't have anything to sort by, we shouldn't be here
56+
if (!sort) {
57+
return
58+
}
59+
60+
// get the sort direction
61+
const direction: 'asc' | 'desc' = fieldName === sort.fieldName
62+
// this is the current sort, so just toggle it
63+
? sort.direction === 'asc' ? 'desc' : 'asc'
64+
// get the default sort for the field... this will never be undefined
65+
: (defaultSortDirectionMap as DefaultSortDirectionMap)[fieldName]
66+
6567
const newSort: Sort = {
66-
direction: sortMap[fieldName] ? 'desc' : 'asc',
68+
direction,
6769
fieldName,
6870
}
6971
setSort(newSort)
7072
}
7173

7274
const headerRow: Array<JSX.Element> = props.columns
7375
.map(col => {
74-
const isCurrentlySorted: boolean = col.propertyName === sort.fieldName
76+
const isSortable: boolean = !!col.propertyName
77+
const isCurrentlySorted: boolean = isSortable && col.propertyName === sort?.fieldName
7578
const colorClass: string = isCurrentlySorted ? 'black-100' : 'black-60'
76-
const sortableClass: string | undefined = !!col.propertyName ? styles.sortable : undefined
79+
const sortableClass: string | undefined = isSortable ? styles.sortable : undefined
7780
return (
7881
<th className={styles.th}>
7982
<div className={classNames(styles['header-container'], styles[col.type], colorClass, sortableClass)}>

src-ts/lib/table/table-column.model.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { TableCellType } from './table-cell.type'
22

33
export interface TableColumn<T> {
4+
readonly defaultSortDirection?: 'asc' | 'desc'
5+
readonly isDefaultSort?: boolean
46
readonly label?: string
57
readonly propertyName?: string
68
readonly renderer?: (data: T, params?: any) => JSX.Element | undefined
+4-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
export { getSorted as tableGetSorted } from './table.functions'
1+
export {
2+
getDefaultSort as tableGetDefaultSort,
3+
getSorted as tableGetSorted,
4+
} from './table.functions'

src-ts/lib/table/table-functions/table.functions.ts

+22-6
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,36 @@
11
import { Sort } from '../../pagination'
22
import { TableColumn } from '../table-column.model'
33

4+
export function getDefaultSort<T>(columns: ReadonlyArray<TableColumn<T>>): Sort | undefined {
5+
6+
const defaultSortColumn: TableColumn<T> | undefined = columns.find(col => col.isDefaultSort)
7+
|| columns.find(col => !!col.propertyName)
8+
9+
const defaultSort: Sort | undefined = !defaultSortColumn?.propertyName
10+
? undefined
11+
: {
12+
direction: defaultSortColumn.defaultSortDirection || 'asc',
13+
fieldName: defaultSortColumn.propertyName,
14+
}
15+
16+
return defaultSort
17+
}
18+
419
export function getSorted<T extends { [propertyName: string]: any }>(
520
data: ReadonlyArray<T>,
6-
sort: Sort,
7-
cols: ReadonlyArray<TableColumn<T>>
21+
cols: ReadonlyArray<TableColumn<T>>,
22+
sort?: Sort,
823
): ReadonlyArray<T> {
924

1025
// get the sort column
11-
const sortColumn: TableColumn<T> | undefined = cols.find(col => col.propertyName === sort.fieldName)
26+
const sortColumn: TableColumn<T> | undefined = !!sort
27+
? cols.find(col => col.propertyName === sort.fieldName)
28+
: undefined
1229

1330
const sortedData: Array<T> = [...data]
1431

15-
// this should never happen, but
16-
// if we didn't find a column, don't sort
17-
if (!sortColumn) {
32+
// if we don't have a column to sort, don't sort
33+
if (!sort || !sortColumn) {
1834
return sortedData
1935
}
2036

src-ts/lib/table/table-sort/TableSort.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ interface TableSortProps {
88
iconClass: string
99
isCurrentlySorted: boolean
1010
propertyName?: string
11-
sort: Sort
11+
sort?: Sort
1212
toggleSort: (fieldName: string) => void
1313
}
1414

1515
const TableSort: FC<TableSortProps> = (props: TableSortProps) => {
1616

17-
if (!props.propertyName) {
17+
if (!props.propertyName || !props.sort) {
1818
return <></>
1919
}
2020

src-ts/lib/tabs-navbar/TabsNavbar.module.scss

+3-7
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33

44
.tabs-wrapper {
55
display: flex;
6-
align-items: center;
7-
background: $blue-15;
6+
background: $blue-10;
87
border-bottom: 1px solid $black-20;
98
margin-bottom: $pad-md;
109

@@ -33,12 +32,9 @@
3332
fill: $blue-25;
3433
}
3534

36-
&:hover {
35+
&:hover:not(:global(.active)) {
3736
color: $black-100;
38-
background-color: $blue-25;
39-
&:global(.active) ~ .active-icon > svg path:last-child {
40-
fill: $blue-25;
41-
}
37+
background-color: $blue-15;
4238
}
4339
}
4440

0 commit comments

Comments
 (0)