From 12db9eb35905ec076e79dd47b87cee4c6acd61a1 Mon Sep 17 00:00:00 2001 From: Justin Gasper Date: Wed, 16 Sep 2026 11:11:17 +1000 Subject: [PATCH] PM-6343 Align Sales search and filter controls --- src/apps/sales/README.md | 6 ++- src/apps/sales/src/SalesPage.module.scss | 11 +++-- src/apps/sales/src/SalesPage.spec.tsx | 11 +---- src/apps/sales/src/SalesPage.tsx | 60 ++++++++++++------------ 4 files changed, 43 insertions(+), 45 deletions(-) diff --git a/src/apps/sales/README.md b/src/apps/sales/README.md index db6d1eb67..31ca74838 100644 --- a/src/apps/sales/README.md +++ b/src/apps/sales/README.md @@ -13,6 +13,8 @@ Report metadata determines every displayed column, including grouped Stage. Search and column substring filters apply on **Apply**; headers sort globally before server pagination. Changing a filter, sort or page size starts at page one. Clear resets filters and sorting. Null display values use an em dash. +Search sales, Filter field and Contains use persistent labels above equal-height +controls, aligned in the desktop filter row and stacked on mobile. The semantic HTML table provides keyboard sorting, `aria-sort`, and a focusable horizontal scroll region for wide reports; the shared Table component performs client sorting and is intentionally not used for this server-paginated report. @@ -25,8 +27,8 @@ are explicit. `allData: false` warns that filters and totals cover only received rows, because Salesforce Analytics caps report details at 2,000 records. The app opts into scoped shared 2026 foundations, using Figtree headings, Nunito -Sans body text, semantic color tokens, shared buttons, inputs and loading -controls. Source design guidance: +Sans body text, semantic color tokens, shared buttons and loading controls, and +explicitly labelled native filter controls. Source design guidance: [Topcoder Design System — August 2026](https://www.figma.com/design/C2cA6508RhpjWJDp7MLKbO/Topcoder-Design-System---Aug-2026?node-id=1-54). Run `nvm use` in platform-ui before `yarn lint`, `LOGICAL_ENV=dev yarn run build`, diff --git a/src/apps/sales/src/SalesPage.module.scss b/src/apps/sales/src/SalesPage.module.scss index 80e29a24c..83de541e5 100644 --- a/src/apps/sales/src/SalesPage.module.scss +++ b/src/apps/sales/src/SalesPage.module.scss @@ -34,9 +34,12 @@ .panelHeader h2 { font-size: $tc-2026-h4-size; line-height: 32px; } .panelHeader p { margin-top: 4px; color: $tc-2026-muted; } .filters { padding: 24px; display: flex; align-items: flex-end; flex-wrap: wrap; gap: 16px; background: $tc-2026-canvas; } -.search { flex: 1 1 220px; min-width: 160px; margin-bottom: 0; } -.selectField { display: flex; flex-direction: column; gap: 8px; font-weight: 700; flex: 1 1 180px; } -.selectField select, .pageControls select { background: $tc-2026-surface; border: 1px solid $tc-2026-border-strong; padding: 10px 12px; min-height: 40px; color: $tc-2026-body; font-weight: 400; max-width: 100%; } +.filterField { display: flex; flex-direction: column; gap: 8px; flex: 1 1 220px; min-width: 160px; } +.filterField label { font-size: 16px; line-height: 24px; font-weight: 700; } +.filterField input, .filterField select, .pageControls select { background: $tc-2026-surface; border: 1px solid $tc-2026-border-strong; padding: 10px 12px; min-height: 40px; color: $tc-2026-body; font-weight: 400; max-width: 100%; } +.filterField input, .filterField select { width: 100%; height: 48px; box-sizing: border-box; font-size: 16px; line-height: 24px; } +.filterField input::placeholder { color: $tc-2026-muted; opacity: 1; } +.filterField input:disabled { background: $tc-2026-border; cursor: not-allowed; } .filterActions { min-height: 48px; } .tableScroll { overflow-x: auto; width: 100%; } .table { width: 100%; border-collapse: collapse; text-align: left; } @@ -68,6 +71,6 @@ .headerActions { width: 100%; justify-content: space-between; } .panelHeader, .filters, .pagination { padding: 16px; } .panelHeader { align-items: flex-start; flex-direction: column; } - .search, .selectField { flex-basis: 100%; } + .filterField { flex-basis: 100%; } .error { align-items: flex-start; flex-direction: column; } } diff --git a/src/apps/sales/src/SalesPage.spec.tsx b/src/apps/sales/src/SalesPage.spec.tsx index 7cdb02112..df79be660 100644 --- a/src/apps/sales/src/SalesPage.spec.tsx +++ b/src/apps/sales/src/SalesPage.spec.tsx @@ -1,7 +1,7 @@ /* eslint-disable import/no-extraneous-dependencies, ordered-imports/ordered-imports */ import '@testing-library/jest-dom' import { act, fireEvent, render, screen, waitFor } from '@testing-library/react' -import { ButtonHTMLAttributes, ChangeEvent, ReactNode } from 'react' +import { ButtonHTMLAttributes, ReactNode } from 'react' import SalesPage from './SalesPage' import { SalesReport } from './sales.models' @@ -19,15 +19,6 @@ jest.mock('~/libs/ui', () => ({ ), IconOutline: { RefreshIcon: () => }, - InputText: (props: { - label: string; disabled?: boolean; name: string; value: string; - onChange: (event: ChangeEvent) => void - }) => ( - - ), LoadingSpinner: (props: { message: string }) => {props.message}, PageTitle: (props: { children: ReactNode }) => {props.children}, }), { virtual: true }) diff --git a/src/apps/sales/src/SalesPage.tsx b/src/apps/sales/src/SalesPage.tsx index 0756d1886..846bbe0fa 100644 --- a/src/apps/sales/src/SalesPage.tsx +++ b/src/apps/sales/src/SalesPage.tsx @@ -4,7 +4,7 @@ /* eslint jsx-a11y/no-noninteractive-tabindex: ["error", { "roles": ["region"] }] */ import { FC, FormEvent, useCallback, useEffect, useRef, useState } from 'react' -import { Button, IconOutline, InputText, LoadingSpinner, PageTitle } from '~/libs/ui' +import { Button, IconOutline, LoadingSpinner, PageTitle } from '~/libs/ui' import { SalesQuery, SalesReport } from './sales.models' import { fetchSalesReport, salesErrorMessage } from './sales.service' @@ -188,20 +188,21 @@ const SalesPage: FC = () => {
- setSearch(event.target.value)} - placeholder='Search all report fields' - tabIndex={0} - type='text' - value={search} - /> -