Skip to content

Commit

Permalink
Merge pull request #1573 from openmsupply/1569-Header-labels-for-mobi…
Browse files Browse the repository at this point in the history
…le-tables

#1569 Header labels for mobile tables
  • Loading branch information
CarlosNZ authored Oct 31, 2023
2 parents 416e6d0 + 5cfe60d commit 1d2d489
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 21 deletions.
17 changes: 7 additions & 10 deletions semantic/src/site/collections/table.overrides
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

@media only screen and (min-width: @largestMobileScreen) {
// For ApplicationList page

#list-container {
padding: 25px 0;
padding: 25px 0;
width: @applicationListWidth;
}

Expand All @@ -31,9 +31,9 @@
}

.ui.table tr {
border: none;
height: 52px;
}
border: none;
height: 52px;
}

.ui.table thead tr {
height: 0;
Expand Down Expand Up @@ -109,7 +109,7 @@
#data-view {
.flex-column();
align-items: center;
width: @applicationListWidth;
width: @applicationListWidth;

.data-view-nav {
width: 90%;
Expand Down Expand Up @@ -153,10 +153,7 @@
}
}

// ********** mobile and small tablet overrides *******************
// ********** mobile and small tablet overrides *******************

@media only screen and (max-width: calc(@largestMobileScreen - 1)) {

}


25 changes: 18 additions & 7 deletions src/components/List/ApplicationsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { useLanguageProvider } from '../../contexts/Localisation'
import { useDeleteApplicationMutation } from '../../utils/generated/graphql'
import { ApplicationListRow, ColumnDetails, SortQuery } from '../../utils/types'
import Loading from '../Loading'
import { TableCellMobileLabelWrapper } from '../../utils/tables/TableCellMobileLabelWrapper'

interface ApplicationsListProps {
columns: Array<ColumnDetails>
columns: ColumnDetails[]
applications: ApplicationListRow[]
sortQuery: SortQuery
handleSort: Function
Expand Down Expand Up @@ -71,7 +72,7 @@ const ApplicationsList: React.FC<ApplicationsListProps> = ({

interface ApplicationRowProps {
refetch: Function
columns: Array<ColumnDetails>
columns: ColumnDetails[]
application: ApplicationListRow
}

Expand All @@ -91,11 +92,21 @@ const ApplicationRow: React.FC<ApplicationRowProps> = ({ refetch, columns, appli

return (
<Table.Row key={`ApplicationList-application-${application.id}`} className="list-row">
{columns.map(({ ColumnComponent }, index) => (
<Table.Cell key={`ApplicationList-row-${application.id}-${index}`}>
<ColumnComponent {...props} />
</Table.Cell>
))}
{columns.map(({ ColumnComponent, headerName, hideMobileLabel, hideOnMobileTest }, index) => {
const rowData = application as Record<string, any>
return (
<Table.Cell key={`ApplicationList-row-${application.id}-${index}`}>
<TableCellMobileLabelWrapper
label={headerName}
rowData={rowData}
hideLabel={hideMobileLabel}
hideCell={hideOnMobileTest}
>
<ColumnComponent {...props} />
</TableCellMobileLabelWrapper>
</Table.Cell>
)
})}
</Table.Row>
)
}
Expand Down
14 changes: 12 additions & 2 deletions src/containers/DataDisplay/DataViewTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import useDebounce from '../../formElementPlugins/search/src/useDebounce'
import { useToast } from '../../contexts/Toast'
import ListFilters from '../List/ListFilters/ListFilters'
import { usePrefs } from '../../contexts/SystemPrefs'
import { TableCellMobileLabelWrapper } from '../../utils/tables/TableCellMobileLabelWrapper'

export type SortColumn = {
title: string
Expand Down Expand Up @@ -171,15 +172,24 @@ const DataViewTableContent: React.FC<DataViewTableContentProps> = ({
</Table.Row>
</Table.Header>
<Table.Body>
{tableRows.map(({ id, rowValues }: { id: number; rowValues: any }) => (
{tableRows.map(({ id, rowValues }) => (
<Table.Row
key={`row_${id}`}
className="clickable"
onClick={() => showDetailsForRow(id)}
>
{rowValues.map((value: any, index: number) => (
<Table.Cell key={`value_${index}`}>
{getCellComponent(value, headerRow[index], id)}
<TableCellMobileLabelWrapper
label={headerRow[index].title}
// These values are not currently dynamic, hence no rowData
// sent
hideLabel={headerRow[index].formatting?.hideLabelOnMobile}
hideCell={headerRow[index].formatting?.hideCellOnMobile}
rowData={{}}
>
{getCellComponent(value, headerRow[index], id)}
</TableCellMobileLabelWrapper>
</Table.Cell>
))}
</Table.Row>
Expand Down
19 changes: 17 additions & 2 deletions src/utils/helpers/list/mapColumnsByRole.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { LIST_COLUMNS, USER_ROLES } from '../../data'
import COLUMNS_PER_ROLE from '../../data/columnsPerUserRole'
import { ColumnDetails } from '../../types'
import { useLanguageProvider } from '../../../contexts/Localisation'
import { ApplicationOutcome, ApplicationStatus } from '../../generated/graphql'

/**
* @function: mapColumnsByRole
Expand All @@ -36,6 +37,7 @@ export const useMapColumnsByRole = () => {
headerName: t('COLUMN_SERIAL'),
sortName: 'serial',
ColumnComponent: SerialNumberCell,
hideMobileLabel: true,
},
LAST_ACTIVE_DATE: {
headerName: t('COLUMN_LAST_ACTIVE_DATE'),
Expand All @@ -48,16 +50,19 @@ export const useMapColumnsByRole = () => {
headerDetail: t('COLUMN_DEADLINE_DATE_TOOLTIP'),
sortName: 'applicant-deadline',
ColumnComponent: DeadlineCell,
hideOnMobileTest: (application) => !application.applicantDeadline,
},
APPLICATION_NAME: {
headerName: t('COLUMN_APPLICATION'),
sortName: 'name',
ColumnComponent: ApplicationNameCell,
hideMobileLabel: true,
},
APPLICATION_NAME_REVIEW_LINK: {
headerName: t('COLUMN_APPLICATION_REVIEW'),
sortName: 'name',
ColumnComponent: ApplicationNameReviewLinkCell,
hideMobileLabel: true,
},
APPLICANT: {
headerName: t('COLUMN_APPLICANT'),
Expand All @@ -78,31 +83,41 @@ export const useMapColumnsByRole = () => {
headerName: t('COLUMN_STAGE'),
sortName: 'stage',
ColumnComponent: StageCell,
hideMobileLabel: true,
},
STATUS: {
headerName: t('COLUMN_STATUS'),
sortName: 'status',
ColumnComponent: StatusCell,
hideMobileLabel: true,
},
OUTCOME: {
headerName: t('COLUMN_OUTCOME'),
sortName: 'outcome',
ColumnComponent: OutcomeCell,
hideMobileLabel: true,
hideOnMobileTest: (application) => application.outcome === ApplicationOutcome.Pending,
},
REVIEWER_ACTION: {
headerName: t('COLUMN_REVIEWER_ACTION'),
sortName: 'outcome',
ColumnComponent: ReviewerActionCell,
hideMobileLabel: true,
hideOnMobileTest: (application) => !application.reviewerAction,
},
APPLICANT_ACTION: {
headerName: t('COLUMN_APPLICANT_ACTION'),
sortName: 'outcome',
ColumnComponent: ApplicantActionCell,
hideMobileLabel: true,
hideOnMobileTest: (application) =>
application.status !== ApplicationStatus.ChangesRequired &&
application.status !== ApplicationStatus.Draft,
},
}

const mapColumnsByRole = (userRoles: USER_ROLES): Array<ColumnDetails> => {
const columns: Array<ColumnDetails> = COLUMNS_PER_ROLE[userRoles].map((key) => allColumns[key])
const mapColumnsByRole = (userRoles: USER_ROLES): ColumnDetails[] => {
const columns: ColumnDetails[] = COLUMNS_PER_ROLE[userRoles].map((key) => allColumns[key])
return columns
}
return mapColumnsByRole
Expand Down
43 changes: 43 additions & 0 deletions src/utils/tables/TableCellMobileLabelWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react'
import { useViewport } from '../../contexts/ViewportState'
import { HideOnMobileTestMethod } from '../types'

/**
* When displayed on mobile devices, table rows become "stacked", in that each
* cell is stacked vertically within a box. This removes any ability to see the
* field names (column headers), so this components provides a simple wrapper
* around table cells to inject a header back in as a label. When not in
* "mobile" view, the table cells will be rendered unchanged
*
* It also has a few options so that this behaviour can be suppressed (in cases
* where the context clearly implies the label), or hidden completely in mobile
* view.
*/

interface MobileLabelWrapperProps {
label: string
rowData: Record<string, unknown>
hideLabel?: boolean
hideCell?: boolean | HideOnMobileTestMethod
}

export const TableCellMobileLabelWrapper: React.FC<MobileLabelWrapperProps> = ({
children,
rowData,
label,
hideCell,
hideLabel = false,
}) => {
const { isMobile } = useViewport()

if (isMobile && ((typeof hideCell === 'function' && hideCell(rowData)) || hideCell === true))
return null

if (!isMobile || hideLabel) return <>{children}</>

return (
<div className="flex-row" style={{ gap: 5, alignItems: 'flex-end' }}>
<strong className="slightly-smaller-text">{label}:</strong> {children}
</div>
)
}
6 changes: 6 additions & 0 deletions src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export {
AssignmentOption,
CellProps,
ChangeRequestsProgress,
HideOnMobileTestMethod,
ColumnDetails,
ContextApplicationState,
CurrentPage,
Expand Down Expand Up @@ -181,11 +182,14 @@ interface CellProps {
deleteApplication: Function
}

type HideOnMobileTestMethod = (rowData: Record<string, unknown>) => boolean
interface ColumnDetails {
headerName: string
headerDetail?: string
sortName: string
ColumnComponent: React.FunctionComponent<any>
hideMobileLabel?: boolean
hideOnMobileTest?: HideOnMobileTestMethod
}

interface ContextApplicationState {
Expand Down Expand Up @@ -699,6 +703,8 @@ interface FormatOptions {
elementParameters?: object
substitution?: string
dateFormat?: DateTimeConstant | DateTimeFormatOptions
hideLabelOnMobile?: boolean
hideCellOnMobile?: boolean
// Add more as required
}

Expand Down

0 comments on commit 1d2d489

Please sign in to comment.