From 499d1a17bd2f10b01e79cdb37bf37f1dee2b28ae Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Fri, 16 May 2025 13:11:09 -0400 Subject: [PATCH 01/52] docs(table): refactors template - creates a TableCellTemplate - creates a TableRowTemplate - creates several helper functions like createCell, createRow - implements some new arguments, such as isSortable, support for more visual elements, different text alignments in the table cells, and the different sorting icons - adds numerical data handling (text alignment) - adds menu disclosure icon to table header cells --- components/table/stories/template.js | 515 +++++++++++++++++---------- 1 file changed, 336 insertions(+), 179 deletions(-) diff --git a/components/table/stories/template.js b/components/table/stories/template.js index 8487cca716d..e04461a6ea7 100644 --- a/components/table/stories/template.js +++ b/components/table/stories/template.js @@ -1,7 +1,10 @@ +import { Template as Avatar } from "@spectrum-css/avatar/stories/template.js"; import { Template as Button } from "@spectrum-css/button/stories/template.js"; import { Template as Checkbox } from "@spectrum-css/checkbox/stories/template.js"; import { Template as Icon } from "@spectrum-css/icon/stories/template.js"; -import { getRandomId } from "@spectrum-css/preview/decorators"; +import { Template as IllustratedMessage } from "@spectrum-css/illustratedmessage/stories/template.js"; +import { getRandomId, renderContent } from "@spectrum-css/preview/decorators"; +import { Template as ProgressCircle } from "@spectrum-css/progresscircle/stories/template.js"; import { Template as Thumbnail } from "@spectrum-css/thumbnail/stories/template.js"; import { classMap } from "lit/directives/class-map.js"; import { ifDefined } from "lit/directives/if-defined.js"; @@ -10,68 +13,202 @@ import { html, literal } from "lit/static-html.js"; import "../index.css"; -export const TableRowItem = ({ - rootClass = "spectrum-Table", - cellContent = "Row Item Text", +/* TableCellTemplate renders a table cell as a div/td/th. */ +export const TableCellTemplate = ({ + rootClass = "spectrum-Table-cell", + cellContent = "", + visualElement, + isFocused = false, + isSelected = false, + isHeaderCell = false, + hasColumnDividers = false, + textAlignment = "start", + hasMenu = false, + useDivs = false, + role, + customClasses = [], + isCollapsible = false, + isLastTier = false, + isExpanded = false, + isSortable = false, + sortableIcon, + ariaControls, +} = {}, context = {}) => { + + const useThumbnail = visualElement === "thumbnail"; + const useAvatar = visualElement === "avatar"; + const useIcon = visualElement === "icon"; + + // Use Table tags or Div tags. + // Note: Lit must use the 'literal' function for dynamic tags to work. + const cellTag = useDivs ? literal`div` : isHeaderCell ? literal`th` : literal`td`; + + // Content for a table cell. + const getCellContent = (columnIndex) => { + const content = Array.isArray(cellContent) + ? cellContent[columnIndex] + : cellContent; + + // Only render visuals in the first two columns + if (columnIndex < 2 && visualElement) { + // options/classes for visual elements + const visuals = { + thumbnail: { + component: Thumbnail, + props: { + size: "75", + imageURL: "example-card-landscape.png", + isCover: true, + }, + containerClass: "spectrum-Table-thumbnailInner", + contentClass: "spectrum-Table-thumbnailContent" + }, + avatar: { + component: Avatar, + props: { + size: "75", + }, + containerClass: "spectrum-Table-avatarInner", + contentClass: "spectrum-Table-avatarContent" + }, + icon: { + component: Icon, + props: { + iconName: "Image", + iconSet: "workflow", + }, + containerClass: "spectrum-Table-iconInner", + contentClass: "spectrum-Table-iconContent" + } + }; + + // Get configuration for the current visual element + const config = visuals[visualElement]; + + if (config) { + return html` +
+ ${config.component(config.props, context)} +
${content}
+
+ `; + } + } + + // Default case - just return the content + return content; + }; + + // The content and classes depend on whether this is a collapsible cell + const cellClasses = { + [`${rootClass}`]: true, + [`${rootClass}--header`]: isHeaderCell ? true : undefined, + [`${rootClass}--thumbnail`]: useThumbnail, + [`${rootClass}--avatar`]: useAvatar, + [`${rootClass}--icon`]: useIcon, + [`${rootClass}--collapsible`]: isCollapsible, + ["is-sortable"]: isSortable && isHeaderCell, + ["is-focused"]: isFocused, + ["is-selected"]: isSelected, + [`${rootClass}--alignEnd`]: textAlignment === "end", + [`${rootClass}--divider`]: hasColumnDividers, + ...customClasses.reduce((a, c) => ({ ...a, [c]: true }), {}), + }; + + const cellContentHtml = isCollapsible + ? html` +
+ ${!isLastTier ? Button({ + size: "m", + iconName: "ChevronRight100", + iconSet: "ui", + hideLabel: true, + customClasses: ["spectrum-Table-disclosureIcon", "spectrum-Button--iconOnly"], + ariaExpanded: isExpanded, + ariaControls, + }, context) : null} +
${getCellContent(1)}
+
+ ` + : isHeaderCell && isSortable + ? html` +
+ ${Icon({ + iconName: sortableIcon === "none" ? undefined : sortableIcon, + iconSet: "workflow", + customClasses: ["spectrum-Table-icon--sort"], + }, context)} + ${getCellContent(1)} +
+ ` + : getCellContent(1); + + return html` + <${cellTag} + class=${classMap(cellClasses)} + role=${ifDefined(useDivs ? "cell" : role ? role : undefined)} + > + ${cellContentHtml} + ${when(hasMenu, () => html` + ${Button({ + size: "m", + iconName: "ChevronDown100", + iconSet: "ui", + hideLabel: true, + customClasses: ["spectrum-Table-disclosureIcon", "spectrum-Button--iconOnly"], + ariaExpanded: isExpanded, + ariaControls, + }, context)} + `)} + + `; +}; + +// TableRowTemplate is used to render a table row as a div/tr. +export const TableRowTemplate = ({ + rootClass = "spectrum-Table-row", + rowContent = [], showCheckbox = false, isSelected = false, + isFocused = false, isSummaryRow = false, isSectionHeader = false, - tableIsEmphasized = true, isCollapsible = false, isExpanded = false, + isSortable = false, + sortableIcon, + isEmphasized = false, isHidden = false, + textAlignment = "start", + selectionMode = "none", hasColumnDividers = false, tier, isLastTier = false, useDivs = false, - showThumbnails = false, isDropTarget = false, ariaControls, customClasses = [], - size = "m", } = {}, context = {}) => { - const useThumbnail = showThumbnails && !isSummaryRow && !isSectionHeader; - const useColumnDividers = hasColumnDividers && !isSummaryRow && !isSectionHeader; // Use Table tags or Div tags. // Note: Lit must use the 'literal' function for dynamic tags to work. const rowTag = useDivs ? literal`div` : literal`tr`; - const cellTag = useDivs ? literal`div` : literal`td`; - // Content for a table cell. - const getCellContent = (columnIndex) => { - const content = Array.isArray(cellContent) - ? cellContent[columnIndex] - : cellContent; - if (useThumbnail && columnIndex < 2) { - return html` -
- ${Thumbnail({ - size: "300", - imageURL: "example-card-landscape.png", - isCover: true, - }, context)} -
${content}
-
- `; - } - else { - return content; - } - }; + // For collapsible rows, we need to pass special props to the first cell + // to render the disclosure button within that cell + const shouldRenderCollapsible = isCollapsible && !isSummaryRow && !isSectionHeader; return html` <${rowTag} class=${classMap({ - [`${rootClass}-row`]: true, - [`${rootClass}-row--summary`]: isSummaryRow, - [`${rootClass}-row--sectionHeader`]: isSectionHeader, - [`${rootClass}-row--collapsible`]: isCollapsible, - [`${rootClass}-row--thumbnail`]: useThumbnail, - [`${rootClass}-cell--divider`]: useColumnDividers, + [`${rootClass}`]: true, + [`${rootClass}--summary`]: isSummaryRow, + [`${rootClass}--sectionHeader`]: isSectionHeader, + [`${rootClass}--collapsible`]: isCollapsible, + ["is-focused"]: isFocused, ["is-selected"]: isSelected, - ["is-expanded"]: isExpanded, + ["is-sortable"]: isSortable && isSectionHeader, + ["is-emphasized"]: isEmphasized, ["is-last-tier"]: isLastTier, ["is-drop-target"]: isDropTarget, ...customClasses.reduce((a, c) => ({ ...a, [c]: true }), {}), @@ -81,112 +218,172 @@ export const TableRowItem = ({ data-tier=${ifDefined(tier)} ?hidden=${isHidden} > - ${when(showCheckbox && !isSectionHeader, () => html` - <${cellTag} - role="gridcell" - class=${classMap({ - [`${rootClass}-cell`]: true, - [`${rootClass}-checkboxCell`]: true, - })} - > - ${when(!isSummaryRow, () => - Checkbox({ - size, - isEmphasized: tableIsEmphasized, - isChecked: isSelected, - customClasses: [`${rootClass}-checkbox`], - }, context) - )} - ` - )} - - ${isCollapsible - ? html` - <${cellTag} - role=${ifDefined(showCheckbox ? "gridcell" : useDivs ? "cell" : undefined)} - class=${classMap({ - [`${rootClass}-cell`]: true, - [`${rootClass}-cell--collapsible`]: true, - [`${rootClass}-cell--thumbnail`]: useThumbnail, - [`${rootClass}-cell--divider`]: useColumnDividers, - })} - > -
- ${when(!isLastTier, () => - Button({ - size, - iconName: "ChevronRight100", - iconSet: "ui", - hideLabel: true, - customClasses: [`${rootClass}-disclosureIcon`], - ariaExpanded: isExpanded, - ariaControls, - }, context) - )} - ${useThumbnail ? getCellContent(0) : html`
${getCellContent(0)}
`} -
- ` - : html` - <${cellTag} - role=${ifDefined(showCheckbox ? "gridcell" : useDivs ? "cell" : undefined)} - class=${classMap({ - [`${rootClass}-cell`]: true, - [`${rootClass}-cell--thumbnail`]: useThumbnail, - [`${rootClass}-cell--divider`]: useColumnDividers, - })} - colspan=${ifDefined(isSectionHeader && showCheckbox ? "4" : isSectionHeader ? "3" : undefined)} - >${getCellContent(0)}` - } + ${when(showCheckbox, () => html` + ${TableCellTemplate({ + useDivs, + hasColumnDividers, + isSortable, + sortableIcon, + textAlignment, + role: "gridcell", + customClasses: [`${rootClass}-checkboxCell`], + cellContent: + Checkbox({ + size: "m", + isFocused, + isChecked: isSelected, + isEmphasized, + isIndeterminate: selectionMode === "multiple" && isSectionHeader ? true : undefined, + customClasses: [`${rootClass}-checkbox`], + }, context) + })}` + )} + + ${when(isSectionHeader, () => html` + ${renderContent(rowContent, { + args: { + useDivs, + hasColumnDividers, + isHeaderCell: true, + isSortable, + sortableIcon, + textAlignment, + }, + })} + `)} + + ${when(!isSectionHeader, () => html` + ${renderContent(rowContent, { + args: { + useDivs, + hasColumnDividers, + isSortable, + sortableIcon, + textAlignment, + // Only pass collapsible props to the first cell + isCollapsible: shouldRenderCollapsible, + isLastTier: shouldRenderCollapsible ? isLastTier : undefined, + isExpanded: shouldRenderCollapsible ? isExpanded : undefined, + ariaControls: shouldRenderCollapsible ? ariaControls : undefined, + }, + })} + `)} - ${when(!isSectionHeader, () => html` - <${cellTag} - role=${ifDefined(showCheckbox ? "gridcell" : useDivs ? "cell" : undefined)} - class=${classMap({ - [`${rootClass}-cell`]: true, - [`${rootClass}-cell--thumbnail`]: useThumbnail, - [`${rootClass}-cell--divider`]: useColumnDividers, - })} - >${getCellContent(1)} - <${cellTag} - role=${ifDefined(showCheckbox ? "gridcell" : useDivs ? "cell" : undefined)} - class=${classMap({ - [`${rootClass}-cell`]: true, - [`${rootClass}-cell--divider`]: useColumnDividers, - })} - >${getCellContent(2)}` - )} `; }; +/* Create a table cell */ +export const createCells = (cells, visualElements = {}, textAlignment = {}, hasMenu = {}) => { + return cells.map((content, index) => { + return (passthroughs, context) => TableCellTemplate({ + ...passthroughs, + cellContent: content, + visualElement: visualElements[index], + textAlignment: textAlignment[index], + hasMenu: hasMenu[index], + }, context); + }); +}; + +/* Create a table row - can be a regular row or header row with sortable columns */ +/* The first row is `thead`, and each object in this array can accept TableRowTemplate props. */ +/* In createRow, the first argument is the TableRow props, and the second is the content of the cells in the row, and the third is options like thumbnails, sortable, etc. */ +export const createRow = (rowProps = {}, cellsData = [], options = {}) => { + const { visualElements = {}, sortableColumns = [], sortableIconNames = {}, textAlignment = {}, hasMenu = {} } = options; + + // If it's a section header row with sortable columns + if (rowProps.isSectionHeader && sortableColumns.length > 0) { + const headerCellContent = cellsData.map((content, index) => { + return (passthroughs, context) => TableCellTemplate({ + ...passthroughs, + cellContent: content, + isHeaderCell: true, + visualElement: visualElements[index], + textAlignment: textAlignment[index], + hasMenu: hasMenu[index], + isSortable: sortableColumns.includes(index), + // Use the icon specified for this column index, or fall back to the default passed from passthroughs + sortableIcon: sortableColumns.includes(index) && sortableIconNames[index] + ? sortableIconNames[index] + : passthroughs.sortableIcon, + }, context); + }); + + return { + ...rowProps, + rowContent: headerCellContent, + }; + } + + return { + ...rowProps, + rowContent: createCells(cellsData, visualElements, textAlignment, hasMenu), + }; +}; + +// Table renders a table as a div/table. export const Template = ({ rootClass = "spectrum-Table", - size = "m", density = "standard", isQuiet = false, - isEmphasized = true, + isEmphasized = false, useDivs = false, + isSortable = false, + sortableIcon, useScroller = false, - showThumbnails = false, + selectionMode = "none", + isLoading = false, isDropTarget = false, hasColumnDividers = false, rowItems = [], customClasses = [], id = getRandomId("table"), } = {}, context = {}) => { - if (!rowItems || !rowItems.length) return html``; + + // empty state table + if (!rowItems || !rowItems.length) return html` + ${IllustratedMessage({ + illustration: "NoData", + illustrationSet: "workflow", + title: "Empty state title", + description: "No data to display. Description of status. Give more information about what a user can do or expect, or how to make items appear here.", + isHorizontal: true, + hasButtons: true, + size: ({ + "compact": "s", + "regular": "m", + "spacious": "l", + }[density] || "regular"), + }, context)} + `; + + // Loading state + if (isLoading) { + return html` + ${ProgressCircle({ + size: ({ + "compact": "s", + "regular": "m", + "spacious": "l", + }[density] || "regular"), + isIndeterminate: true, + }, context)} + `; + } // Use Table tags or Div tags. const tableTag = useDivs ? literal`div` : literal`table`; const theadTag = useDivs ? literal`div` : literal`thead`; const tbodyTag = useDivs ? literal`div` : literal`tbody`; - const rowTag = useDivs ? literal`div` : literal`tr`; - const thTag = useDivs ? literal`div` : literal`th`; + + // If it's a section header, use the first item as thead content + const firstRowContent = rowItems && rowItems.length > 0 ? rowItems[0] : null; + const remainingRowContent = rowItems && rowItems.length > 1 ? rowItems.slice(1) : []; const rootClassMapVariants = { - [`${rootClass}--size${size?.toUpperCase()}`]: typeof size !== "undefined", - [`${rootClass}--${density}`]: density !== "standard", + [`${rootClass}--${density}`]: density !== "regular", [`${rootClass}--quiet`]: isQuiet, [`${rootClass}--emphasized`]: isEmphasized, ...customClasses.reduce((a, c) => ({ ...a, [c]: true }), {}), @@ -210,60 +407,18 @@ export const Template = ({ class="${rootClass}-head" role=${ifDefined(useDivs ? "rowgroup" : undefined)} > - <${rowTag} - role=${ifDefined(useDivs ? "row" : undefined)} - > - ${when(useCheckboxCell, () => html` - <${thTag} - class="spectrum-Table-headCell spectrum-Table-checkboxCell" - role=${ifDefined(useDivs ? "columnheader" : undefined)} - > - ${Checkbox({ - size, - isEmphasized: isEmphasized, - isChecked: false, - isIndeterminate: true, - customClasses: [`${rootClass}-checkbox`], - }, context)} - ` - )} - <${thTag} - class="${rootClass}-headCell is-sortable is-sorted-desc" - role=${ifDefined(useDivs ? "columnheader" : undefined)} - aria-sort="descending" - tabindex="0" - > - ${Icon({ - iconName: "ArrowDown100", - setName: "ui", - size, - customClasses: [`${rootClass}-sortedIcon`], - }, context)}Column title${ - Icon({ - iconName: "ChevronDown100", - setName: "ui", - size, - customClasses: [`${rootClass}-menuIcon`], - }, context)} - - <${thTag} - class="${rootClass}-headCell is-sortable" - role=${ifDefined(useDivs ? "columnheader" : undefined)} - aria-sort="none" - tabindex="0" - > - ${Icon({ - iconName: "ArrowDown100", - setName: "ui", - size, - customClasses: [`${rootClass}-sortedIcon`], - }, context)}Column title - - <${thTag} - class="${rootClass}-headCell" - role=${ifDefined(useDivs ? "columnheader" : undefined)} - >Column title - + ${TableRowTemplate({ + useDivs, + hasColumnDividers, + isEmphasized, + isQuiet, + isSortable, + sortableIcon, + selectionMode, + showCheckbox: selectionMode !== "none", + // First row is the header row. + ...firstRowContent, + })} <${tbodyTag} class=${classMap({ @@ -272,14 +427,16 @@ export const Template = ({ })} role=${ifDefined(useDivs ? "rowgroup" : undefined)} > - ${rowItems.map((item) => - TableRowItem({ - rootClass, - size, + ${remainingRowContent.map((item) => + TableRowTemplate({ useDivs, - showThumbnails, hasColumnDividers, - tableIsEmphasized: isEmphasized, + isEmphasized, + isQuiet, + selectionMode, + isSortable, + sortableIcon, + showCheckbox: selectionMode !== "none", ...item, }, context) )} From 053a37188c66d7a8a37659e823978f15af21813e Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Fri, 16 May 2025 13:21:04 -0400 Subject: [PATCH 02/52] docs(table): refactors story files - uses new approach for creating table cells and rows (createRow()) - new args implemented like isSortable, visual elements, isLoading,etc. - new stories for sortable columns, selection modes (previously known as multi-select), empty state, loading - removes any dropzone stories from autodocs since design isn't certain the S2 table will support those. - adds numerical data alignment story - adds hasMenu story (disclosure icon) --- components/table/stories/table.stories.js | 896 +++++++++++++--------- 1 file changed, 541 insertions(+), 355 deletions(-) diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index 834a90f4630..a1d4d389d96 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -1,10 +1,9 @@ -import { Sizes } from "@spectrum-css/preview/decorators"; import { disableDefaultModes } from "@spectrum-css/preview/modes"; -import { isEmphasized, isQuiet, size } from "@spectrum-css/preview/types"; +import { isEmphasized, isLoading, isQuiet } from "@spectrum-css/preview/types"; import metadata from "../dist/metadata.json"; import packageJson from "../package.json"; import { TableGroup } from "./table.test.js"; -import { Template } from "./template.js"; +import { createRow, Template } from "./template.js"; /** * A table is used to create a container for displaying information. It allows users to sort, compare, and take action on large amounts of data. @@ -13,7 +12,6 @@ export default { title: "Table", component: "Table", argTypes: { - size: size(["s", "m", "l", "xl"]), density: { name: "Density", table: { @@ -25,10 +23,11 @@ export default { }, isQuiet, isEmphasized, + isLoading, useDivs: { name: "Use divs for markup", description: - "Use 'div' elements for all of the table markup instead of the 'table' element.", + "Use `div` elements for all of the table markup instead of the `table` element.", type: { name: "boolean" }, table: { type: { summary: "boolean" }, @@ -47,17 +46,37 @@ export default { }, control: "boolean", }, - showThumbnails: { - name: "Show thumbnails in content", - description: - "Uses the thumbnail component at the start of the first column's cells.", - type: { name: "boolean" }, + isSortable: { + name: "Sortable column", + description: "If a table column is sortable, the header cell displays a sort icon.", table: { type: { summary: "boolean" }, category: "Component", }, control: "boolean", }, + sortableIcon: { + name: "Sortable icon", + description: "The icon to use for the sortable column.", + table: { + type: { summary: "string" }, + category: "Component", + }, + options: ["Sort", "SortDown", "SortUp", "none"], + control: "select", + if: { arg: "isSortable", eq: true }, + }, + selectionMode: { + name: "Selection mode", + description: "Determines whether items in the table can be selected, and if users can select only one or multiple items.", + type: { name: "string", required: true }, + table: { + type: { summary: "string" }, + category: "Selection", + }, + options: ["none", "single", "multiple"], + control: "select", + }, hasColumnDividers: { name: "Show dividers between columns", description: "Sets dividers between table columns.", @@ -74,6 +93,7 @@ export default { table: { type: { summary: "boolean" }, category: "State", + disable: true, }, control: "boolean", }, @@ -83,32 +103,18 @@ export default { }, args: { rootClass: "spectrum-Table", - size: "m", - density: "standard", + density: "regular", isQuiet: false, - isEmphasized: true, + isLoading: false, + isEmphasized: false, + selectionMode: "none", useDivs: false, - showThumbnails: false, isDropTarget: false, useScroller: false, hasColumnDividers: false, - rowItems: [ - { - cellContent: "Row item alpha", - }, - { - cellContent: "Row item bravo", - }, - { - cellContent: "Row item charlie", - }, - { - cellContent: "Row item delta", - }, - { - cellContent: "Row item echo", - }, - ], + isSortable: false, + sortableIcon: "", + rowItems: [], }, parameters: { design: { @@ -120,58 +126,51 @@ export default { }, }; -const ExampleRowItems = [ - { - cellContent: ["Table row alpha", "Alpha", "Table row alpha"], - showCheckbox: true, - }, - { - cellContent: [ - "Table row bravo. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", - "Bravo", - "Table row bravo. Lorem ipsum dolor sit amet.", - ], - showCheckbox: true, - isSelected: true, - }, - { - cellContent: "Table row charlie", - showCheckbox: true, - isSelected: true, - }, - { - cellContent: "Table row delta", - showCheckbox: true, - }, - { - cellContent: "Echo", - showCheckbox: true, - }, - { - cellContent: "Foxtrot", - showCheckbox: true, - }, -]; - /** - * The medium size is the default and recommended option. The default table also uses the regular density. + * The default table also uses the regular density. Similar to a paragraph of text, textual data is always left-aligned within a table. Never use center alignment. */ export const Default = TableGroup.bind({}); -Default.args = {}; +Default.args = { + rowItems: [ + /* In createRow, the first argument is the TableRow props, and the second is the content of the cells in the row, and the third is options like thumbnails, sortable, etc. */ + createRow( + {isSectionHeader: true}, + ["Column 1", "Column 2", "Column 3"] + ), + createRow( + {isSummaryRow: true}, + ["Summary row", "Summary 2", "Summary 3"] + ), + createRow( + {}, + ["Table row alpha", "Column 2", "Column 3"] + ), + createRow( + {}, + ["Table row charlie", "Column 2", "Column 3"] + ), + ], +}; // ********* DOCS ONLY ********* // /** - * Tables come in four different sizes: small, medium, large, and extra-large. The medium size is the default and recommended option. + * The empty state variant displays an [illustrated message](/docs/components-illustrated-message--docs) when there is no data to display. */ -export const Sizing = (args, context) => Sizes({ - Template, - withHeading: false, - withBorder: false, - ...args, -}, context); -Sizing.args = {}; -Sizing.tags = ["!dev"]; -Sizing.parameters = { +export const EmptyState = Template.bind({}); +EmptyState.args = { + rowItems: [], +}; +EmptyState.parameters = { + chromatic: { disableSnapshot: true }, +}; + +export const Loading = Template.bind({}); +Loading.args = { + ...Default.args, + isLoading: true, +}; +Loading.tags = ["!dev"]; +Loading.parameters = { chromatic: { disableSnapshot: true }, }; @@ -204,33 +203,99 @@ Spacious.parameters = { Spacious.storyName = "Density - spacious"; /** - * The standard multi-select table includes a column of checkboxes used for selecting rows. + * The standard multi-select table includes a column of checkboxes used for selecting rows. When the selection mode + * is set to `multiple`, users may select more than one table row. */ export const MultiSelect = Template.bind({}); -MultiSelect.storyName = "Multi-select"; +MultiSelect.storyName = "Selection mode: multiple"; MultiSelect.args = { - rowItems: ExampleRowItems, + ...Default.args, + selectionMode: "multiple", }; +MultiSelect.tags = ["!dev"]; MultiSelect.parameters = { chromatic: { disableSnapshot: true }, }; /** - * Excluding the `.spectrum-Table--emphasized` class will change the style of selected rows. + * When the selection mode is set to `single`, users may select only one table row. */ -export const NonEmphasizedMultiSelect = Template.bind({}); -NonEmphasizedMultiSelect.storyName = "Non-emphasized multi-select"; -NonEmphasizedMultiSelect.args = { +export const SingleSelect = Template.bind({}); +SingleSelect.storyName = "Selection mode: single"; +SingleSelect.args = { + ...Default.args, + selectionMode: "single", +}; +SingleSelect.tags = ["!dev"]; +SingleSelect.parameters = { + chromatic: { disableSnapshot: true }, +}; + +/** + * Including the `.spectrum-Table--emphasized` class will change the style of selected rows. + */ +export const EmphasizedMultiSelect = Template.bind({}); +EmphasizedMultiSelect.storyName = "Selection mode: multiple, emphasized styling"; +EmphasizedMultiSelect.args = { ...MultiSelect.args, - isEmphasized: false, + isEmphasized: true, +}; +EmphasizedMultiSelect.tags = ["!dev"]; +EmphasizedMultiSelect.parameters = { + chromatic: { disableSnapshot: true }, +}; + +/** + * Numerical data should generally be end-aligned for the ease of scanning and comparing. Numerical data should only be start-aligned when numbers are arbitrary identifiers, known as “nominal numbers,” which means they can’t be compared or combined arithmetically (e.g., ZIP codes, IP addresses, phone numbers). Column headers follow the alignment of the data. + */ +export const NumericalData = Template.bind({}); +NumericalData.args = { + rowItems: [ + createRow( + {isSectionHeader: true}, + ["Pokemon", "Type", "Health"], + { + textAlignment: { + 2: "end" + } + } + ), + createRow( + {}, + ["Pikachu", "Electric", "35"], + { + textAlignment: { + 2: "end" + } + } + ), + createRow( + {}, + ["Charmander", "Fire", "39"], + { + textAlignment: { + 2: "end" + } + } + ), + createRow( + {}, + ["Mew", "Psychic", "100"], + { + textAlignment: { + 2: "end" + } + } + ), + ], }; -NonEmphasizedMultiSelect.tags = ["!dev"]; -NonEmphasizedMultiSelect.parameters = { +NumericalData.storyName = "Numerical data"; +NumericalData.parameters = { chromatic: { disableSnapshot: true }, }; /** - * The quiet table has a transparent background and no borders on the left and right. + * Quiet tables are for when a table is meant to be supplementary, subtle, or lightweight. The quiet table utilizes the `.spectrum-Table--quiet` class and has a transparent background and no borders on the left and right. */ export const Quiet = Template.bind({}); Quiet.args = { @@ -249,14 +314,17 @@ export const QuietMultiSelect = Template.bind({}); QuietMultiSelect.args = { ...MultiSelect.args, isQuiet: true, + selectionMode: "multiple", }; QuietMultiSelect.tags = ["!dev"]; -QuietMultiSelect.storyName = "Quiet multi-select"; +QuietMultiSelect.storyName = "Selection mode: multiple, quiet styling"; QuietMultiSelect.parameters = { chromatic: { disableSnapshot: true }, }; /** + * Column dividers are for organizing table content and aid the user in parsing related data. These are optional; use them carefully to group related content. + * * The standard table can display column dividers by including the `.spectrum-Table-cell--divider` class with `.spectrum-Table-cell`. Use sparingly to group related content. */ export const WithColumnDividers = Template.bind({}); @@ -276,23 +344,31 @@ WithColumnDividers.parameters = { export const SummaryAndSelected = Template.bind({}); SummaryAndSelected.args = { rowItems: [ - { - cellContent: "Table row alpha", - }, - { - cellContent: "Table row bravo", - }, - { - cellContent: "Table row charlie", - isSelected: true, - }, - { - cellContent: "Table row delta", - }, - { - cellContent: "Summary row", - isSummaryRow: true, - }, + createRow( + {isSectionHeader: true}, + ["Column 1", "Column 2", "Column 3"], + { + hasMenu: { + 0: true, + } + } + ), + createRow( + {}, + ["Table row alpha", "Column 2", "Column 3"] + ), + createRow( + {isSelected: true}, + ["Table row bravo (selected)", "Column 2", "Column 3"] + ), + createRow( + {}, + ["Table row charlie", "Column 2", "Column 3"] + ), + createRow( + {isSummaryRow: true}, + ["Summary row", "Summary 2", "Summary 3"] + ) ], }; SummaryAndSelected.storyName = "Summary and selected"; @@ -306,29 +382,31 @@ SummaryAndSelected.parameters = { export const SectionHeader = Template.bind({}); SectionHeader.args = { rowItems: [ - { - cellContent: "Section header", - isSectionHeader: true, - }, - { - cellContent: "Table row alpha", - }, - { - cellContent: "Table row bravo", - }, - { - cellContent: "Table row charlie", - }, - { - cellContent: "Another section header", - isSectionHeader: true, - }, - { - cellContent: "Table row delta", - }, - { - cellContent: "Table row echo", - }, + createRow( + {isSectionHeader: true}, + ["Section header", "Column 2", "Column 3"], + { + hasMenu: { + 1: true + } + } + ), + createRow( + {}, + ["Table row alpha", "Column 2", "Column 3"] + ), + createRow( + {}, + ["Table row bravo", "Column 2", "Column 3"] + ), + createRow( + {isSectionHeader: true}, + ["Another section header", "Column 2", "Column 3"] + ), + createRow( + {}, + ["Table row charlie", "Column 2", "Column 3"] + ) ], }; SectionHeader.storyName = "Section header"; @@ -347,7 +425,6 @@ SectionHeaderQuiet.parameters = { }; /** - * * A table can be wrapped in a fixed height `div` with the `.spectrum-Table-scroller` class. This allows scrolling of the table body and makes the column headers sticky (i.e. fixed to the top on scroll). * * When using the scrollable wrapper, the column headers must have a solid background color set. This can be customized to match the parent background with the custom property `--mod-table-header-background-color-scrollable`. @@ -360,29 +437,39 @@ export const Scrollable = Template.bind({}); Scrollable.args = { useScroller: true, rowItems: [ - { - cellContent: "Table row alpha", - }, - { - cellContent: "Table row bravo", - }, - { - cellContent: "Table row charlie", - isSelected: true, - }, - { - cellContent: "Table row delta", - }, - { - cellContent: "Table row echo", - }, - { - cellContent: "Table row foxtrot", - }, - { - cellContent: "Summary row", - isSummaryRow: true, - }, + createRow( + {isSectionHeader: true}, + ["Column 1", "Column 2", "Column 3"] + ), + createRow( + {}, + ["Table row alpha", "Column 2", "Column 3"] + ), + createRow( + {}, + ["Table row bravo", "Column 2", "Column 3"] + ), + createRow( + {isSelected: true}, + ["Table row charlie (selected)", "Column 2", "Column 3"] + ), + createRow( + {}, + ["Table row delta", "Column 2", "Column 3"] + ), + createRow( + {}, + ["Table row echo", "Column 2", "Column 3"] + ), + createRow( + {}, + ["Table row foxtrot", "Column 2", "Column 3"] + ), + createRow( + {isSummaryRow: true}, + ["Summary row", "Column 2", "Column 3"] + ) + ], }; Scrollable.tags = ["!dev"]; @@ -398,7 +485,6 @@ DivsScrollable.storyName = "Scrollable with divs"; DivsScrollable.args = { ...Scrollable.args, useDivs: true, - rowItems: ExampleRowItems, }; DivsScrollable.tags = ["!dev"]; DivsScrollable.parameters = { @@ -411,60 +497,67 @@ DivsScrollable.parameters = { export const Collapsible = Template.bind({}); Collapsible.args = { rowItems: [ - { - cellContent: "Table row alpha", - isCollapsible: true, - isExpanded: true, - tier: 0, - ariaControls: "table-cr-bravo table-cr-delta", - id: "table-cr-alpha", - }, - { - cellContent: - "Table row bravo. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", - isCollapsible: true, - tier: 1, - ariaControls: "table-cr-charlie", - id: "table-cr-bravo", - }, - { - cellContent: [ - "Table row charlie", - "Default not visible", - "Default not visible", - ], - isCollapsible: true, - isHidden: true, - tier: 2, - id: "table-cr-charlie", - }, - { - cellContent: "Table row delta", - isSelected: true, - isCollapsible: true, - isExpanded: true, - tier: 1, - ariaControls: "table-cr-echo table-cr-foxtrot", - id: "table-cr-delta", - }, - { - cellContent: "Table row echo", - tier: 2, - isLastTier: true, - isCollapsible: true, - id: "table-cr-echo", - }, - { - cellContent: "Table row foxtrot", - tier: 2, - isLastTier: true, - isCollapsible: true, - id: "table-cr-foxtrot", - }, - { - cellContent: "Summary row", - isSummaryRow: true, - }, + createRow( + {isSectionHeader: true}, + ["Collapsible options", "Column 2", "Column 3"] + ), + createRow( + { + isCollapsible: true, + isExpanded: true, + tier: 0, + ariaControls: "table-cr-bravo table-cr-delta", + id: "table-cr-alpha" + }, + ["Table row alpha", "Column 2", "Column 3"] + ), + createRow( + { + isCollapsible: true, + tier: 1, + ariaControls: "table-cr-charlie", + id: "table-cr-bravo" + }, + ["Table row bravo", "Column 2", "Column 3"] + ), + createRow( + { + isCollapsible: true, + isHidden: true, + tier: 2, + id: "table-cr-charlie" + }, + ["Table row charlie", "Column 2", "Column 3"] + ), + createRow( + { + isSelected: true, + isCollapsible: true, + isExpanded: true, + tier: 1, + ariaControls: "table-cr-echo table-cr-foxtrot", + id: "table-cr-delta" + }, + ["Table row delta", "Column 2", "Column 3"] + ), + createRow( + { + tier: 2, + isLastTier: true, + isCollapsible: true, + id: "table-cr-echo" + }, + ["Table row echo", "Column 2", "Column 3"] + ), + createRow( + { + tier: 2, + isLastTier: true, + isCollapsible: true, + id: "table-cr-foxtrot" + }, + ["Table row foxtrot", "Column 2", "Column 3"] + ) ], }; Collapsible.parameters = { @@ -472,70 +565,69 @@ Collapsible.parameters = { }; export const CollapsibleMultiSelect = Template.bind({}); -CollapsibleMultiSelect.storyName = "Collapsible multi-select"; +CollapsibleMultiSelect.storyName = "Selection mode: multiple, collapsible rows"; CollapsibleMultiSelect.args = { + selectionMode: "multiple", rowItems: [ - { - showCheckbox: true, - cellContent: "Table row alpha", - isCollapsible: true, - isExpanded: true, - tier: 0, - ariaControls: "table-cr-bravo table-cr-delta", - id: "table-cr-alpha", - }, - { - showCheckbox: true, - cellContent: - "Table row bravo. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", - isCollapsible: true, - tier: 1, - ariaControls: "table-cr-charlie", - id: "table-cr-bravo", - }, - { - showCheckbox: true, - cellContent: [ - "Table row charlie", - "Default not visible", - "Default not visible", - ], - isCollapsible: true, - isHidden: true, - tier: 2, - id: "table-cr-charlie", - }, - { - showCheckbox: true, - cellContent: "Table row delta", - isSelected: true, - isCollapsible: true, - isExpanded: true, - tier: 1, - ariaControls: "table-cr-echo table-cr-foxtrot", - id: "table-cr-delta", - }, - { - showCheckbox: true, - cellContent: "Table row echo", - tier: 2, - isLastTier: true, - isCollapsible: true, - id: "table-cr-echo", - }, - { - showCheckbox: true, - cellContent: "Table row foxtrot", - tier: 2, - isLastTier: true, - isCollapsible: true, - id: "table-cr-foxtrot", - }, - { - showCheckbox: true, - cellContent: "Summary row", - isSummaryRow: true, - }, + createRow( + {isSectionHeader: true}, + ["Collapsible options", "Column 2", "Column 3"] + ), + createRow( + { + isCollapsible: true, + isExpanded: true, + tier: 0, + ariaControls: "table-ms-bravo table-ms-charlie", + id: "table-ms-alpha" + }, + ["Table row alpha", "Column 2", "Column 3"] + ), + createRow( + { + isCollapsible: true, + tier: 1, + id: "table-ms-bravo" + }, + ["Table row bravo", "Column 2", "Column 3"] + ), + createRow( + { + isCollapsible: true, + isSelected: true, + tier: 1, + isExpanded: true, + ariaControls: "table-ms-delta table-ms-echo", + id: "table-ms-charlie" + }, + ["Table row charlie (selected)", "Column 2", "Column 3"] + ), + createRow( + { + tier: 2, + isLastTier: true, + isCollapsible: true, + id: "table-ms-delta" + }, + ["Table row delta", "Column 2", "Column 3"] + ), + createRow( + { + tier: 2, + isLastTier: true, + isCollapsible: true, + id: "table-ms-echo" + }, + ["Table row echo", "Column 2", "Column 3"] + ), + createRow( + { + isCollapsible: true, + tier: 0, + id: "table-ms-foxtrot" + }, + ["Table row foxtrot", "Column 2", "Column 3"] + ) ], }; CollapsibleMultiSelect.parameters = { @@ -543,91 +635,177 @@ CollapsibleMultiSelect.parameters = { }; /** - * Thumbnails can be used in the table content, with some additional markup and classes for alignment. + * Thumbnails, avatars, and other icons can be used in the table content, with some additional markup and classes for alignment. */ -export const Thumbnails = Template.bind({}); -Thumbnails.args = { - showThumbnails: true, +export const Visuals = Template.bind({}); +Visuals.args = { rowItems: [ - { - cellContent: ["Table row alpha", "Test", "2"], - }, - { - cellContent: ["Table row bravo", "Test", "28"], - }, - { - cellContent: [ - "Table row charlie. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", - "Test", - "23", - ], - }, - { - cellContent: ["Table row delta", "Test", "7"], - }, - ], + createRow( + {isSectionHeader: true}, + ["Avatar", "Icon", "Thumbnail"] + ), + createRow( + {}, + ["Avatar Example", "Icons", "Thumbnail Example"], + { + visualElements: { + 0: "avatar", + 1: "icon", + 2: "thumbnail" + } + } + ), + createRow( + {}, + ["Avatar Example", "Icons", "Thumbnail Example"], + { + visualElements: { + 0: "avatar", + 1: "icon", + 2: "thumbnail" + } + } + ), + createRow( + {}, + ["Avatar Example", "Icons", "Thumbnail Example"], + { + visualElements: { + 0: "avatar", + 1: "icon", + 2: "thumbnail" + } + } + ) + ] +}; +Visuals.parameters = { + chromatic: { disableSnapshot: true }, }; -Thumbnails.tags = ["!dev"]; -Thumbnails.parameters = { + +/** + * The table can also combine visuals with collapsible rows. + */ +export const VisualsCollapsible = Template.bind({}); +VisualsCollapsible.args = { + rowItems: [ + createRow( + {isSectionHeader: true}, + ["Visual options", "Column 2", "Column 3"] + ), + createRow( + { + isCollapsible: true, + isExpanded: true, + tier: 0, + ariaControls: "table-vc-bravo table-vc-charlie", + id: "table-vc-alpha" + }, + ["Visual row alpha", "Column 2", "Column 3"], + { + visualElements: { + 0: "avatar", + 1: "icon", + 2: "thumbnail" + } + } + ), + createRow( + { + isCollapsible: true, + tier: 1, + id: "table-vc-bravo" + }, + ["Visual row bravo", "Column 2", "Column 3"], + { + visualElements: { + 0: "icon", + 1: "avatar", + 2: "thumbnail" + } + } + ), + createRow( + { + isCollapsible: true, + isExpanded: true, + tier: 1, + ariaControls: "table-vc-delta", + id: "table-vc-charlie" + }, + ["Visual row charlie", "Column 2", "Column 3"], + { + visualElements: { + 0: "thumbnail", + 1: "avatar", + 2: "icon" + } + } + ), + createRow( + { + tier: 2, + isLastTier: true, + isCollapsible: true, + id: "table-vc-delta" + }, + ["Visual row delta", "Column 2", "Column 3"], + { + visualElements: { + 0: "avatar", + 1: "icon", + 2: "thumbnail" + } + } + ) + ] +}; +VisualsCollapsible.storyName = "Visuals: collapsible"; +VisualsCollapsible.parameters = { chromatic: { disableSnapshot: true }, }; /** - * The thumbnail table variant can also be combined with collapsible rows. + * Tables with sortable columns can show different states of sorting: unsorted, ascending, and descending. */ -export const ThumbnailsCollapsible = Template.bind({}); -ThumbnailsCollapsible.args = { - showThumbnails: true, +export const SortIcons = Template.bind({}); +SortIcons.args = { rowItems: [ - { - cellContent: "Table row alpha", - isCollapsible: true, - isExpanded: true, - tier: 0, - ariaControls: "table-cr-bravo", - id: "table-cr-alpha", - }, - { - cellContent: - "Table row bravo. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", - isCollapsible: true, - tier: 1, - ariaControls: "table-cr-charlie", - id: "table-cr-bravo", - }, - { - cellContent: "Table row charlie", - tier: 2, - isLastTier: true, - isCollapsible: true, - id: "table-cr-charlie", - }, - { - cellContent: "Table row delta", - tier: 2, - isLastTier: true, - isCollapsible: true, - id: "table-cr-delta", - }, - { - cellContent: "Summary row", - isSummaryRow: true, - }, + createRow( + { isSectionHeader: true }, + ["Default sort", "Ascending sort", "Descending sort"], + { + sortableColumns: [0, 1, 2], + sortableIconNames: { + 0: "Sort", + 1: "SortUp", + 2: "SortDown", + } + } + ), + createRow({}, ["Data A", "Data B", "Data C"]), + createRow({}, ["Data D", "Data E", "Data F"]), + createRow({}, ["Data G", "Data H", "Data I"]), ], + isSortable: true, }; -ThumbnailsCollapsible.storyName = "Thumbnails: collapsible"; -ThumbnailsCollapsible.parameters = { +SortIcons.tags = ["!dev"]; +SortIcons.storyName = "Sort icons"; +SortIcons.parameters = { chromatic: { disableSnapshot: true }, }; + +// TODO: The design team doesn't have dropzones in the table component, so they are removed from the docs page for now. /** * The table body can accept dropped content. */ export const BodyDropZone = Template.bind({}); BodyDropZone.args = { + ...Default.args, isDropTarget: true, }; -BodyDropZone.tags = ["!dev"]; +BodyDropZone.tags = ["!autodocs","!dev"]; BodyDropZone.storyName = "Dropzone: body"; BodyDropZone.parameters = { chromatic: { disableSnapshot: true }, @@ -639,29 +817,37 @@ BodyDropZone.parameters = { export const RowDropZone = Template.bind({}); RowDropZone.args = { rowItems: [ - { - cellContent: "Table row alpha", - isDropTarget: true, - }, - { - cellContent: "Table row bravo", - }, - { - cellContent: "Table row charlie", - isDropTarget: true, - }, - { - cellContent: "Table row delta", - }, - { - cellContent: "Table row echo", - }, - { - cellContent: "Table row foxtrot", - isDropTarget: true, - }, + createRow( + {isSectionHeader: true}, + ["Column 1", "Column 2", "Column 3"] + ), + createRow( + {isDropTarget: true}, + ["Table row alpha", "Column 2", "Column 3"] + ), + createRow( + {}, + ["Table row bravo", "Column 2", "Column 3"] + ), + createRow( + {isDropTarget: true}, + ["Table row charlie", "Column 2", "Column 3"] + ), + createRow( + {}, + ["Table row delta", "Column 2", "Column 3"] + ), + createRow( + {}, + ["Table row echo", "Column 2", "Column 3"] + ), + createRow( + {isDropTarget: true}, + ["Table row foxtrot", "Column 2", "Column 3"] + ) ], }; +RowDropZone.tags = ["!autodocs","!dev"]; RowDropZone.storyName = "Dropzone: row"; RowDropZone.parameters = { chromatic: { disableSnapshot: true }, @@ -672,7 +858,7 @@ RowDropZoneQuiet.args = { ...RowDropZone.args, isQuiet: true, }; -RowDropZoneQuiet.tags = ["!dev"]; +RowDropZoneQuiet.tags = ["!autodocs","!dev"]; RowDropZoneQuiet.storyName = "Dropzone: row, quiet"; RowDropZoneQuiet.parameters = { chromatic: { disableSnapshot: true }, From 886df179ec4bb63f20d96b99f0458d5bc87fb792 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Fri, 16 May 2025 13:30:06 -0400 Subject: [PATCH 03/52] feat(table): adds new table styles - consolidates repetitive code - moves custom properties to top of file - moves high contrast styles to bottom of file --- components/table/index.css | 541 +++++++++++++++---------------------- 1 file changed, 221 insertions(+), 320 deletions(-) diff --git a/components/table/index.css b/components/table/index.css index 7ffbacf046a..fe7a27657b5 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -1,5 +1,5 @@ /*! - * Copyright 2024 Adobe. All rights reserved. + * Copyright 2025 Adobe. All rights reserved. * * This file is licensed to you under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. You may obtain a copy @@ -12,401 +12,218 @@ */ .spectrum-Table { + /* Animation */ + --spectrum-table-transition-duration: var(--spectrum-animation-duration-100); + + /* Background colors */ --spectrum-table-header-background-color: var(--spectrum-transparent-white-25); - --spectrum-table-border-color: var(--spectrum-gray-200); - --spectrum-table-divider-color: var(--spectrum-gray-200); --spectrum-table-row-background-color: var(--spectrum-gray-25); - --spectrum-table-summary-row-background-color: var(--spectrum-gray-100); + /* @todo Refactor or remove these properties once the RGB stripped value is available. */ + --spectrum-table-row-background-color-hover: rgb(var(--spectrum-gray-900-rgb), var(--spectrum-table-row-hover-opacity)); + --spectrum-table-summary-row-background-color: var(--spectrum-gray-200); --spectrum-table-section-header-background-color: var(--spectrum-gray-100); - --spectrum-table-icon-color-focus: var(--spectrum-neutral-subdued-content-color-key-focus); - --spectrum-table-icon-color-focus-hover: var(--spectrum-neutral-subdued-content-color-hover); -} - -.spectrum-Table--quiet { - --spectrum-table-header-background-color: var(--spectrum-transparent-white-25); - --spectrum-table-row-background-color: var(--spectrum-transparent-white-25); -} - -/********* HIGH CONTRAST *********/ -@media (forced-colors: active) { - .spectrum-Table { - --highcontrast-table-row-background-color: Canvas; - --highcontrast-table-row-text-color: CanvasText; - --highcontrast-table-divider-color: CanvasText; - --highcontrast-table-border-color: CanvasText; - --highcontrast-table-icon-color: CanvasText; - --highcontrast-table-icon-color-focus: Highlight; - - --highcontrast-table-selected-row-background-color: Highlight; - --highcontrast-table-selected-row-text-color: HighlightText; - - @supports (color: SelectedItem) { - --highcontrast-table-selected-row-background-color: SelectedItem; - --highcontrast-table-selected-row-text-color: SelectedItemText; - } - - --highcontrast-table-selected-row-background-color-focus: Highlight; - --highcontrast-table-selected-row-text-color-focus: HighlightText; - --highcontrast-table-row-background-color-hover: Highlight; - --highcontrast-table-row-text-color-hover: HighlightText; - - --highcontrast-table-section-header-text-color: Canvas; - --highcontrast-table-section-header-background-color: CanvasText; - - --highcontrast-table-focus-indicator-color: Highlight; - --highcontrast-table-transition-duration: 0; - } + --spectrum-table-selected-row-background-color: rgb(var(--spectrum-blue-900-rgb), var(--spectrum-table-selected-row-background-opacity)); + --spectrum-table-selected-row-background-color-non-emphasized: rgb(var(--spectrum-gray-700-rgb), var(--spectrum-table-selected-row-background-opacity-non-emphasized)); + --spectrum-table-selected-row-background-color-focus: rgb(var(--spectrum-blue-900-rgb), var(--spectrum-table-selected-row-background-opacity-hover)); + --spectrum-table-selected-row-background-color-non-emphasized-focus: rgb(var(--spectrum-gray-700-rgb), var(--spectrum-table-selected-row-background-opacity-non-emphasized-hover)); - .spectrum-Table-cell { - /* Removes unstylable readability backplate. */ - forced-color-adjust: none; - } - - .spectrum-Table-row { - &:hover, - &:focus-visible, - &.is-focused { - .spectrum-Table-checkbox .spectrum-Checkbox-box::before { - outline: var(--highcontrast-table-row-text-color-hover) 1px solid; - } - } - } + --spectrum-table-cell-background-color: var(--highcontrast-table-row-background-color, var(--mod-table-row-background-color, var(--spectrum-table-row-background-color))); + --spectrum-table-selected-cell-background-color: var(--highcontrast-table-selected-row-background-color, var(--mod-table-selected-row-background-color-non-emphasized, var(--spectrum-table-selected-row-background-color-non-emphasized))); - .spectrum-Table-row.is-selected, - .spectrum-Table-row.is-drop-target, - .spectrum-Table-body.is-drop-target .spectrum-Table-row { - /* Ensure negative offset outline contrasts on top of SelectedItem background. */ - --highcontrast-table-cell-focus-indicator-color: var(--highcontrast-table-selected-row-text-color); - --highcontrast-table-cell-focus-extra-offset: 1px; + --spectrum-table-drop-zone-background-color: rgb(var(--spectrum-drop-zone-background-color-rgb), var(--spectrum-drop-zone-background-color-opacity)); + --spectrum-table-drop-zone-outline-color: var(--spectrum-accent-visual-color); - .spectrum-Table-checkbox .spectrum-Checkbox-box::before { - outline: var(--highcontrast-table-selected-row-text-color) 1px solid; - } - } -} + /* Row Selection */ + --spectrum-table-row-active-color: rgb(var(--spectrum-gray-900-rgb), var(--spectrum-table-row-down-opacity)); /* active/down state background color */ -.spectrum-Table { - --spectrum-table-cell-inline-space: var(--spectrum-table-edge-to-content); + --spectrum-table-header-row-checkbox-block-spacing: var(--spectrum-table-header-row-checkbox-to-top-medium); + --spectrum-table-row-checkbox-block-spacing: var(--spectrum-table-row-checkbox-to-top-medium-regular); - --spectrum-table-border-radius: var(--spectrum-corner-radius-100); + /* Border */ + --spectrum-table-border-color: var(--spectrum-gray-300); + --spectrum-table-border-radius: var(--spectrum-corner-radius-medium-size-extra-small); --spectrum-table-border-width: var(--spectrum-table-border-divider-width); --spectrum-table-outer-border-inline-width: var(--spectrum-table-border-divider-width); - --spectrum-table-default-vertical-align: top; - --spectrum-table-header-vertical-align: middle; - - --spectrum-table-header-font-weight: var(--spectrum-bold-font-weight); + /* Divider */ + --spectrum-table-divider-color: var(--spectrum-gray-300); - --spectrum-table-row-font-family: var(--spectrum-sans-font-family-stack); - --spectrum-table-row-font-weight: var(--spectrum-regular-font-weight); - --spectrum-table-row-font-style: var(--spectrum-default-font-style); - --spectrum-table-row-line-height: var(--spectrum-line-height-100); + /* Cells */ + --spectrum-table-cell-inline-space: var(--spectrum-table-edge-to-content); - --spectrum-table-border-color: var(--spectrum-gray-300); - --spectrum-table-divider-color: var(--spectrum-gray-300); - --spectrum-table-header-background-color: var(--spectrum-transparent-white-100); - --spectrum-table-header-text-color: var(--spectrum-body-color); - --spectrum-table-row-background-color: var(--spectrum-gray-50); - --spectrum-table-row-text-color: var(--spectrum-neutral-content-color-default); + /* Focus indicators */ + --spectrum-table-focus-indicator-thickness: var(--spectrum-focus-indicator-thickness); + --spectrum-table-focus-indicator-color: var(--spectrum-focus-indicator-color); - /* @todo Refactor or remove these properties once the RGB stripped value is available. */ - --spectrum-table-selected-row-background-color: rgba(var(--spectrum-blue-900-rgb), var(--spectrum-table-selected-row-background-opacity)); - --spectrum-table-selected-row-background-color-non-emphasized: rgba(var(--spectrum-gray-700-rgb), var(--spectrum-table-selected-row-background-opacity-non-emphasized)); - --spectrum-table-row-background-color-hover: rgba(var(--spectrum-gray-900-rgb), var(--spectrum-table-row-hover-opacity)); - --spectrum-table-row-active-color: rgba(var(--spectrum-gray-900-rgb), var(--spectrum-table-row-down-opacity)); - --spectrum-table-selected-row-background-color-focus: rgba(var(--spectrum-blue-900-rgb), var(--spectrum-table-selected-row-background-opacity-hover)); - --spectrum-table-selected-row-background-color-non-emphasized-focus: rgba(var(--spectrum-gray-700-rgb), var(--spectrum-table-selected-row-background-opacity-non-emphasized-hover)); + --spectrum-table-selected-cell-background-color-focus: var(--highcontrast-table-selected-row-background-color-focus, var(--mod-table-selected-row-background-color-non-emphasized-focus, var(--spectrum-table-selected-row-background-color-non-emphasized-focus))); + /* Visual (avatar, icons, thumbnails) */ --spectrum-table-icon-color-default: var(--spectrum-neutral-subdued-content-color-default); --spectrum-table-icon-color-hover: var(--spectrum-neutral-subdued-content-color-hover); --spectrum-table-icon-color-active: var(--spectrum-neutral-subdued-content-color-down); --spectrum-table-icon-color-focus: var(--spectrum-neutral-subdued-content-color-key-focus); --spectrum-table-icon-color-focus-hover: var(--spectrum-neutral-subdued-content-color-key-focus); --spectrum-table-icon-color-key-focus: var(--spectrum-neutral-subdued-content-color-key-focus); + --spectrum-table-disclosure-icon-size: var(--spectrum-component-height-100); + --spectrum-table-icon-color: var(--highcontrast-table-icon-color, var(--mod-table-icon-color-default, var(--spectrum-table-icon-color-default))); - --spectrum-table-focus-indicator-thickness: var(--spectrum-focus-indicator-thickness); - --spectrum-table-focus-indicator-color: var(--spectrum-focus-indicator-color); + /* Alignment */ + --spectrum-table-default-vertical-align: top; + --spectrum-table-header-vertical-align: middle; - --spectrum-table-drop-zone-background-color: rgba(var(--spectrum-drop-zone-background-color-rgb), var(--spectrum-drop-zone-background-color-opacity)); - --spectrum-table-drop-zone-outline-color: var(--spectrum-accent-visual-color); + /* Font */ + --spectrum-table-header-font-weight: var(--spectrum-bold-font-weight); + --spectrum-table-header-text-color: var(--spectrum-body-color); - --spectrum-table-transition-duration: var(--spectrum-animation-duration-100); + --spectrum-table-row-font-family: var(--spectrum-sans-font-family-stack); + --spectrum-table-row-font-weight: var(--spectrum-regular-font-weight); + --spectrum-table-row-font-style: var(--spectrum-default-font-style); + --spectrum-table-row-line-height: var(--spectrum-line-height-100); + --spectrum-table-row-text-color: var(--spectrum-neutral-content-color-default); + --spectrum-table-row-font-size: var(--spectrum-font-size-100); - /* Summary Row and Section Header Row */ --spectrum-table-summary-row-font-weight: var(--spectrum-bold-font-weight); - --spectrum-table-summary-row-background-color: var(--spectrum-gray-200); - --spectrum-table-section-header-font-weight: var(--spectrum-bold-font-weight); - --spectrum-table-section-header-background-color: var(--spectrum-gray-200); - - /* Collapsible and thumbnails */ - --spectrum-table-collapsible-tier-indent: var(--spectrum-spacing-300); - --spectrum-table-collapsible-disclosure-inline-spacing: 0px; - --spectrum-table-collapsible-icon-animation-duration: var(--spectrum-animation-duration-100); - - --spectrum-table-cell-background-color: var(--highcontrast-table-row-background-color, var(--mod-table-row-background-color, var(--spectrum-table-row-background-color))); - --spectrum-table-selected-cell-background-color: var(--highcontrast-table-selected-row-background-color, var(--mod-table-selected-row-background-color-non-emphasized, var(--spectrum-table-selected-row-background-color-non-emphasized))); - --spectrum-table-selected-cell-background-color-focus: var(--highcontrast-table-selected-row-background-color-focus, var(--mod-table-selected-row-background-color-non-emphasized-focus, var(--spectrum-table-selected-row-background-color-non-emphasized-focus))); - - /* @passthrough */ - --mod-thumbnail-size: var(--mod-table-thumbnail-size, var(--spectrum-table-thumbnail-size)); - &:dir(rtl) { - --spectrum-logical-rotation: matrix(-1, 0, 0, 1, 0, 0); - } -} - -.spectrum-Table, -.spectrum-Table--sizeM { - --spectrum-table-min-header-height: var(--spectrum-component-height-100); - --spectrum-table-header-top-to-text: var(--spectrum-table-column-header-row-top-to-text-medium); - --spectrum-table-header-bottom-to-text: var(--spectrum-table-column-header-row-bottom-to-text-medium); + /* Size and Spacing */ + --spectrum-table-min-header-row-height: var(--spectrum-component-height-100); + --spectrum-table-header-row-top-to-text: var(--spectrum-table-column-header-row-top-to-text-medium); + --spectrum-table-header-row-bottom-to-text: var(--spectrum-table-column-header-row-bottom-to-text-medium); + --spectrum-table-icon-to-text: var(--spectrum-text-to-visual-300); - --spectrum-table-min-row-height: var(--spectrum-table-row-height-medium-regular); + --spectrum-table-min-row-height: var(--spectrum-table-row-height-medium); --spectrum-table-row-top-to-text: var(--spectrum-table-row-top-to-text-medium-regular); --spectrum-table-row-bottom-to-text: var(--spectrum-table-row-bottom-to-text-medium-regular); - --spectrum-table-icon-to-text: var(--spectrum-text-to-visual-100); - - /* Typography */ - --spectrum-table-row-font-size: var(--spectrum-font-size-100); - - /* Row Selection */ - --spectrum-table-header-checkbox-block-spacing: var(--spectrum-table-header-row-checkbox-to-top-medium); - --spectrum-table-row-checkbox-block-spacing: var(--spectrum-table-row-checkbox-to-top-medium-regular); - - /* Summary Row and Section Header Row */ --spectrum-table-section-header-min-height: var(--spectrum-table-section-header-row-height-medium); --spectrum-table-section-header-block-start-spacing: var(--spectrum-component-top-to-text-100); --spectrum-table-section-header-block-end-spacing: var(--spectrum-component-bottom-to-text-100); - /* Collapsible and Thumbnails */ - --spectrum-table-disclosure-icon-size: var(--spectrum-component-height-100); - --spectrum-table-thumbnail-block-spacing: var(--spectrum-table-thumbnail-to-top-minimum-medium-regular); - --spectrum-table-thumbnail-to-text: var(--spectrum-text-to-visual-100); + --spectrum-table-visual-to-text: var(--spectrum-text-to-visual-300); --spectrum-table-thumbnail-size: var(--spectrum-thumbnail-size-300); -} - -.spectrum-Table--sizeS { - /* Size and Spacing */ - --spectrum-table-min-header-height: var(--spectrum-component-height-100); - --spectrum-table-header-top-to-text: var(--spectrum-table-column-header-row-top-to-text-small); - --spectrum-table-header-bottom-to-text: var(--spectrum-table-column-header-row-bottom-to-text-small); - - --spectrum-table-min-row-height: var(--spectrum-table-row-height-small-regular); - --spectrum-table-row-top-to-text: var(--spectrum-table-row-top-to-text-small-regular); - --spectrum-table-row-bottom-to-text: var(--spectrum-table-row-bottom-to-text-small-regular); - --spectrum-table-icon-to-text: var(--spectrum-text-to-visual-100); + --spectrum-table-checkbox-to-cell-content: var(--spectrum-table-checkbox-to-text); - /* Typography */ - --spectrum-table-row-font-size: var(--spectrum-font-size-75); - - /* Row Selection */ - --spectrum-table-header-checkbox-block-spacing: var(--spectrum-table-header-row-checkbox-to-top-small); - --spectrum-table-row-checkbox-block-spacing: var(--spectrum-table-row-checkbox-to-top-small-regular); - - /* Summary Row and Section Header Row */ - --spectrum-table-section-header-min-height: var(--spectrum-table-section-header-row-height-small); - --spectrum-table-section-header-block-start-spacing: var(--spectrum-component-top-to-text-75); - --spectrum-table-section-header-block-end-spacing: var(--spectrum-component-bottom-to-text-75); + --spectrum-table-collapsible-tier-indent: var(--spectrum-spacing-300); + --spectrum-table-collapsible-disclosure-inline-spacing: 0px; + --spectrum-table-collapsible-icon-animation-duration: var(--spectrum-animation-duration-100); - /* Collapsible and Thumbnails */ - --spectrum-table-disclosure-icon-size: var(--spectrum-component-height-75); + /* @passthrough */ + --mod-thumbnail-size: var(--mod-table-thumbnail-size, var(--spectrum-table-thumbnail-size)); - --spectrum-table-thumbnail-block-spacing: var(--spectrum-table-thumbnail-to-top-minimum-small-regular); - --spectrum-table-thumbnail-to-text: var(--spectrum-text-to-visual-100); - --spectrum-table-thumbnail-size: var(--spectrum-thumbnail-size-200); + &:dir(rtl) { + --spectrum-logical-rotation: matrix(-1, 0, 0, 1, 0, 0); + } } -.spectrum-Table--sizeL { +/********* VARIANTS *********/ +.spectrum-Table--compact { /* Size and Spacing */ - --spectrum-table-min-header-height: var(--spectrum-component-height-200); - --spectrum-table-header-top-to-text: var(--spectrum-table-column-header-row-top-to-text-large); - --spectrum-table-header-bottom-to-text: var(--spectrum-table-column-header-row-bottom-to-text-large); - - --spectrum-table-min-row-height: var(--spectrum-table-row-height-large-regular); - --spectrum-table-row-top-to-text: var(--spectrum-table-row-top-to-text-large-regular); - --spectrum-table-row-bottom-to-text: var(--spectrum-table-row-bottom-to-text-large-regular); - - --spectrum-table-icon-to-text: var(--spectrum-text-to-visual-200); - - /* Typography */ - --spectrum-table-row-font-size: var(--spectrum-font-size-200); + --spectrum-table-min-row-height: var(--mod-table-min-row-height-compact, var(--spectrum-table-row-height-medium-compact)); + --spectrum-table-header-row-top-to-text: var(--mod-table-row-top-to-text-compact, var(--spectrum-table-row-top-to-text-medium-compact)); + --spectrum-table-header-row-bottom-to-text: var(--mod-table-row-bottom-to-text-compact, var(--spectrum-table-row-bottom-to-text-medium-compact)); /* Row Selection */ - --spectrum-table-header-checkbox-block-spacing: var(--spectrum-table-header-row-checkbox-to-top-large); - --spectrum-table-row-checkbox-block-spacing: var(--spectrum-table-row-checkbox-to-top-large-regular); - - /* Summary Row and Section Header Row */ - --spectrum-table-section-header-min-height: var(--spectrum-table-section-header-row-height-large); - --spectrum-table-section-header-block-start-spacing: var(--spectrum-component-top-to-text-200); - --spectrum-table-section-header-block-end-spacing: var(--spectrum-component-bottom-to-text-200); + --spectrum-table-row-checkbox-block-spacing: var(--mod-table-row-checkbox-block-spacing-compact, var(--spectrum-table-row-checkbox-to-top-medium-compact)); /* Collapsible and Thumbnails */ - --spectrum-table-disclosure-icon-size: var(--spectrum-component-height-200); - - --spectrum-table-thumbnail-block-spacing: var(--spectrum-table-thumbnail-to-top-minimum-large-regular); - --spectrum-table-thumbnail-to-text: var(--spectrum-text-to-visual-200); - --spectrum-table-thumbnail-size: var(--spectrum-thumbnail-size-500); + --spectrum-table-thumbnail-block-spacing: var(--mod-table-thumbnail-block-spacing-compact, var(--spectrum-table-thumbnail-to-top-minimum-medium-compact)); + --mod-table-thumbnail-size: var(--mod-table-thumbnail-size-compact, var(--spectrum-thumbnail-size-200)); } -.spectrum-Table--sizeXL { +.spectrum-Table--spacious { /* Size and Spacing */ - --spectrum-table-min-header-height: var(--spectrum-component-height-300); - --spectrum-table-header-top-to-text: var(--spectrum-table-column-header-row-top-to-text-extra-large); - --spectrum-table-header-bottom-to-text: var(--spectrum-table-column-header-row-bottom-to-text-extra-large); - - --spectrum-table-min-row-height: var(--spectrum-table-row-height-extra-large-regular); - --spectrum-table-row-top-to-text: var(--spectrum-table-row-top-to-text-extra-large-regular); - --spectrum-table-row-bottom-to-text: var(--spectrum-table-row-bottom-to-text-extra-large-regular); - - --spectrum-table-icon-to-text: var(--spectrum-text-to-visual-300); - - /* Typography */ - --spectrum-table-row-font-size: var(--spectrum-font-size-300); + --spectrum-table-min-row-height: var(--mod-table-min-row-height-spacious, var(--spectrum-table-row-height-medium-spacious)); + --spectrum-table-header-row-top-to-text: var(--mod-table-row-top-to-text-spacious, var(--spectrum-table-row-top-to-text-medium-spacious)); + --spectrum-table-header-row-bottom-to-text: var(--mod-table-row-bottom-to-text-spacious, var(--spectrum-table-row-bottom-to-text-medium-spacious)); /* Row Selection */ - --spectrum-table-header-checkbox-block-spacing: var(--spectrum-table-header-row-checkbox-to-top-extra-large); - --spectrum-table-row-checkbox-block-spacing: var(--spectrum-table-row-checkbox-to-top-extra-large-regular); - - /* Summary Row and Section Header Row */ - --spectrum-table-section-header-min-height: var(--spectrum-table-section-header-row-height-extra-large); - --spectrum-table-section-header-block-start-spacing: var(--spectrum-component-top-to-text-300); - --spectrum-table-section-header-block-end-spacing: var(--spectrum-component-bottom-to-text-300); + --spectrum-table-row-checkbox-block-spacing: var(--mod-table-row-checkbox-block-spacing-spacious, var(--spectrum-table-row-checkbox-to-top-medium-spacious)); /* Collapsible and Thumbnails */ - --spectrum-table-disclosure-icon-size: var(--spectrum-component-height-300); - - --spectrum-table-thumbnail-block-spacing: var(--spectrum-table-thumbnail-to-top-minimum-extra-large-regular); - --spectrum-table-thumbnail-to-text: var(--spectrum-text-to-visual-300); - --spectrum-table-thumbnail-size: var(--spectrum-thumbnail-size-700); + --spectrum-table-thumbnail-block-spacing: var(--mod-table-thumbnail-block-spacing-spacious, var(--spectrum-table-thumbnail-to-top-minimum-medium-spacious)); + --mod-table-thumbnail-size: var(--mod-table-thumbnail-size-spacious, var(--spectrum-thumbnail-size-500)); } -/********* VARIANTS *********/ -.spectrum-Table--compact { - /* Size and Spacing */ - --mod-table-min-row-height: var(--mod-table-min-row-height--compact, var(--spectrum-table-row-height-medium-compact)); - --mod-table-row-top-to-text: var(--mod-table-row-top-to-text--compact, var(--spectrum-table-row-top-to-text-medium-compact)); - --mod-table-row-bottom-to-text: var(--mod-table-row-bottom-to-text--compact, var(--spectrum-table-row-bottom-to-text-medium-compact)); - - /* Row Selection */ - --mod-table-row-checkbox-block-spacing: var(--mod-table-row-checkbox-block-spacing--compact, var(--spectrum-table-row-checkbox-to-top-medium-compact)); - - /* Collapsible and Thumbnails */ - --mod-table-thumbnail-block-spacing: var(--mod-table-thumbnail-block-spacing-compact, var(--spectrum-table-thumbnail-to-top-minimum-medium-compact)); - --mod-table-thumbnail-size: var(--mod-table-thumbnail-size-compact, var(--spectrum-thumbnail-size-200)); - - &.spectrum-Table--sizeS { - /* Size and Spacing */ - --mod-table-min-row-height: var(--mod-table-min-row-height--compact, var(--spectrum-table-row-height-small-compact)); - --mod-table-row-top-to-text: var(--mod-table-row-top-to-text--compact, var(--spectrum-table-row-top-to-text-small-compact)); - --mod-table-row-bottom-to-text: var(--mod-table-row-bottom-to-text--compact, var(--spectrum-table-row-bottom-to-text-small-compact)); +.spectrum-Table--emphasized, +.spectrum-Table-row .is-emphasized { + --spectrum-table-selected-cell-background-color: var(--highcontrast-table-selected-row-background-color, var(--mod-table-selected-row-background-color, var(--spectrum-table-selected-row-background-color))); + --spectrum-table-selected-cell-background-color-focus: var(--highcontrast-table-selected-row-background-color-focus, var(--mod-table-selected-row-background-color-focus, var(--spectrum-table-selected-row-background-color-focus))); +} - /* Row Selection */ - --mod-table-row-checkbox-block-spacing: var(--mod-table-row-checkbox-block-spacing--compact, var(--spectrum-table-row-checkbox-to-top-small-compact)); +.spectrum-Table--quiet { + --spectrum-table-border-radius: var(--mod-table-border-radius-quiet, 0px); + --spectrum-table-outer-border-inline-width: var(--mod-table-outer-border-inline-width-quiet, 0px); + --spectrum-table-header-background-color: var(--mod-table-header-background-color-quiet, var(--spectrum-transparent-white-100)); + --spectrum-table-row-background-color: var(--mod-table-row-background-color-quiet, var(--spectrum-transparent-white-100)); + --spectrum-table-border-color: transparent; +} - /* Collapsible and Thumbnails */ - --mod-table-thumbnail-block-spacing: var(--mod-table-thumbnail-block-spacing-compact, var(--spectrum-table-thumbnail-to-top-minimum-small-compact)); - --mod-table-thumbnail-size: var(--mod-table-thumbnail-size-compact, var(--spectrum-thumbnail-size-50)); +.spectrum-Table-row { + &:hover, + &:focus-visible, + &.is-focused { + --highcontrast-table-row-text-color: var(--highcontrast-table-row-text-color-hover); + --highcontrast-table-icon-color: var(--highcontrast-table-row-text-color-hover); + --spectrum-table-cell-background-color: var(--highcontrast-table-row-background-color-hover, var(--mod-table-row-background-color-hover, var(--spectrum-table-row-background-color-hover))); } - &.spectrum-Table--sizeL { - /* Size and Spacing */ - --mod-table-min-row-height: var(--mod-table-min-row-height--compact, var(--spectrum-table-row-height-large-compact)); - --mod-table-row-top-to-text: var(--mod-table-row-top-to-text--compact, var(--spectrum-table-row-top-to-text-large-compact)); - --mod-table-row-bottom-to-text: var(--mod-table-row-bottom-to-text--compact, var(--spectrum-table-row-bottom-to-text-large-compact)); - - /* Row Selection */ - --mod-table-row-checkbox-block-spacing: var(--mod-table-row-checkbox-block-spacing--compact, var(--spectrum-table-row-checkbox-to-top-large-compact)); - - /* Collapsible and Thumbnails */ - --mod-table-thumbnail-block-spacing: var(--mod-table-thumbnail-block-spacing-compact, var(--spectrum-table-thumbnail-to-top-minimum-large-compact)); - --mod-table-thumbnail-size: var(--mod-table-thumbnail-size-compact, var(--spectrum-thumbnail-size-300)); + &:active { + --highcontrast-table-row-text-color: var(--highcontrast-table-row-text-color-hover); + --highcontrast-table-icon-color: var(--highcontrast-table-row-text-color-hover); + --spectrum-table-cell-background-color: var(--highcontrast-table-row-background-color-hover, var(--mod-table-row-active-color, var(--spectrum-table-row-active-color))); } - &.spectrum-Table--sizeXL { - /* Size and Spacing */ - --mod-table-min-row-height: var(--mod-table-min-row-height--compact, var(--spectrum-table-row-height-extra-large-compact)); - --mod-table-row-top-to-text: var(--mod-table-row-top-to-text--compact, var(--spectrum-table-row-top-to-text-extra-large-compact)); - --mod-table-row-bottom-to-text: var(--mod-table-row-bottom-to-text--compact, var(--spectrum-table-row-bottom-to-text-extra-large-compact)); - - /* Row Selection */ - --mod-table-row-checkbox-block-spacing: var(--mod-table-row-checkbox-block-spacing--compact, var(--spectrum-table-row-checkbox-to-top-extra-large-compact)); + &.is-selected { + --highcontrast-table-row-text-color: var(--highcontrast-table-selected-row-text-color); + --highcontrast-table-icon-color: var(--highcontrast-table-selected-row-text-color); + --spectrum-table-cell-background-color: var(--highcontrast-table-selected-row-background-color, var(--spectrum-table-selected-cell-background-color)); - /* Collapsible and Thumbnails */ - --mod-table-thumbnail-block-spacing: var(--mod-table-thumbnail-block-spacing-compact, var(--spectrum-table-thumbnail-to-top-minimum-extra-large-compact)); - --mod-table-thumbnail-size: var(--mod-table-thumbnail-size-compact, var(--spectrum-thumbnail-size-500)); + &:hover, + &:focus-visible, + &.is-focused { + --highcontrast-table-row-text-color: var(--highcontrast-table-selected-row-text-color-focus); + --highcontrast-table-icon-color: var(--highcontrast-table-selected-row-text-color-focus); + --spectrum-table-cell-background-color: var(--highcontrast-table-selected-row-background-color-focus, var(--spectrum-table-selected-cell-background-color-focus)); + } } -} - -.spectrum-Table--spacious { - /* Size and Spacing */ - --mod-table-min-row-height: var(--mod-table-min-row-height--spacious, var(--spectrum-table-row-height-medium-spacious)); - --mod-table-row-top-to-text: var(--mod-table-row-top-to-text--spacious, var(--spectrum-table-row-top-to-text-medium-spacious)); - --mod-table-row-bottom-to-text: var(--mod-table-row-bottom-to-text--spacious, var(--spectrum-table-row-bottom-to-text-medium-spacious)); - - /* Row Selection */ - --mod-table-row-checkbox-block-spacing: var(--mod-table-row-checkbox-block-spacing--spacious, var(--spectrum-table-row-checkbox-to-top-medium-spacious)); - - /* Collapsible and Thumbnails */ - --mod-table-thumbnail-block-spacing: var(--mod-table-thumbnail-block-spacing-spacious, var(--spectrum-table-thumbnail-to-top-minimum-medium-spacious)); - --mod-table-thumbnail-size: var(--mod-table-thumbnail-size-spacious, var(--spectrum-thumbnail-size-500)); - - &.spectrum-Table--sizeS { - /* Size and Spacing */ - --mod-table-min-row-height: var(--mod-table-min-row-height--spacious, var(--spectrum-table-row-height-small-spacious)); - --mod-table-row-top-to-text: var(--mod-table-row-top-to-text--spacious, var(--spectrum-table-row-top-to-text-small-spacious)); - --mod-table-row-bottom-to-text: var(--mod-table-row-bottom-to-text--spacious, var(--spectrum-table-row-bottom-to-text-small-spacious)); - - /* Row Selection */ - --mod-table-row-checkbox-block-spacing: var(--mod-table-row-checkbox-block-spacing--spacious, var(--spectrum-table-row-checkbox-to-top-small-spacious)); - /* Collapsible and Thumbnails */ - --mod-table-thumbnail-block-spacing: var(--mod-table-thumbnail-block-spacing-spacious, var(--spectrum-table-thumbnail-to-top-minimum-small-spacious)); - --mod-table-thumbnail-size: var(--mod-table-thumbnail-size-spacious, var(--spectrum-thumbnail-size-300)); + &.is-drop-target, + .spectrum-Table-body.is-drop-target & { + --highcontrast-table-row-text-color: var(--highcontrast-table-selected-row-text-color); + --highcontrast-table-icon-color: var(--highcontrast-table-selected-row-text-color); + --spectrum-table-cell-background-color: var(--highcontrast-table-selected-row-background-color, var(--mod-table-drop-zone-background-color, var(--spectrum-table-drop-zone-background-color))); } +} - &.spectrum-Table--sizeL { - /* Size and Spacing */ - --mod-table-min-row-height: var(--mod-table-min-row-height--spacious, var(--spectrum-table-row-height-large-spacious)); - --mod-table-row-top-to-text: var(--mod-table-row-top-to-text--spacious, var(--spectrum-table-row-top-to-text-large-spacious)); - --mod-table-row-bottom-to-text: var(--mod-table-row-bottom-to-text--spacious, var(--spectrum-table-row-bottom-to-text-large-spacious)); - - /* Row Selection */ - --mod-table-row-checkbox-block-spacing: var(--mod-table-row-checkbox-block-spacing--spacious, var(--spectrum-table-row-checkbox-to-top-large-spacious)); +.spectrum-Table-headCell { + &.is-sortable { + &:hover { + --spectrum-table-icon-color: var(--highcontrast-table-icon-color-focus, var(--mod-table-icon-color-hover, var(--spectrum-table-icon-color-hover))); + } - /* Collapsible and Thumbnails */ - --mod-table-thumbnail-block-spacing: var(--mod-table-thumbnail-block-spacing-spacious, var(--spectrum-table-thumbnail-to-top-minimum-large-spacious)); - --mod-table-thumbnail-size: var(--mod-table-thumbnail-size-spacious, var(--spectrum-thumbnail-size-700)); - } + &:active { + --spectrum-table-icon-color: var(--highcontrast-table-icon-color-focus, var(--mod-table-icon-color-active, var(--spectrum-table-icon-color-active))); + } - &.spectrum-Table--sizeXL { - /* Size and Spacing */ - --mod-table-min-row-height: var(--mod-table-min-row-height--spacious, var(--spectrum-table-row-height-extra-large-spacious)); - --mod-table-row-top-to-text: var(--mod-table-row-top-to-text--spacious, var(--spectrum-table-row-top-to-text-extra-large-spacious)); - --mod-table-row-bottom-to-text: var(--mod-table-row-bottom-to-text--spacious, var(--spectrum-table-row-bottom-to-text-extra-large-spacious)); + &:focus { + --spectrum-table-icon-color: var(--highcontrast-table-icon-color-focus, var(--mod-table-icon-color-focus, var(--spectrum-table-icon-color-focus))); + } - /* Row Selection */ - --mod-table-row-checkbox-block-spacing: var(--mod-table-row-checkbox-block-spacing--spacious, var(--spectrum-table-row-checkbox-to-top-extra-large-spacious)); + &:focus:hover { + --spectrum-table-icon-color: var(--highcontrast-table-icon-color-focus, var(--mod-table-icon-color-focus-hover, var(--spectrum-table-icon-color-focus-hover))); + } - /* Collapsible and Thumbnails */ - --mod-table-thumbnail-block-spacing: var(--mod-table-thumbnail-block-spacing-spacious, var(--spectrum-table-thumbnail-to-top-minimum-extra-large-spacious)); - --mod-table-thumbnail-size: var(--mod-table-thumbnail-size-spacious, var(--spectrum-thumbnail-size-800)); + &:focus-visible, + &.is-keyboardFocused { + --spectrum-table-icon-color: var(--highcontrast-table-icon-color-focus, var(--mod-table-icon-color-key-focus, var(--spectrum-table-icon-color-key-focus))); + } } } -.spectrum-Table--emphasized { - --spectrum-table-selected-cell-background-color: var(--highcontrast-table-selected-row-background-color, var(--mod-table-selected-row-background-color, var(--spectrum-table-selected-row-background-color))); - --spectrum-table-selected-cell-background-color-focus: var(--highcontrast-table-selected-row-background-color-focus, var(--mod-table-selected-row-background-color-focus, var(--spectrum-table-selected-row-background-color-focus))); -} - -.spectrum-Table--quiet { - --mod-table-border-radius: var(--mod-table-border-radius--quiet, 0px); - --mod-table-outer-border-inline-width: var(--mod-table-outer-border-inline-width--quiet, 0px); - --mod-table-header-background-color: var(--mod-table-header-background-color--quiet, var(--spectrum-transparent-white-100)); - --mod-table-row-background-color: var(--mod-table-row-background-color--quiet, var(--spectrum-transparent-white-100)); +.spectrum-Table-row--summary { + --spectrum-table-cell-background-color: var(--highcontrast-table-row-background-color, var(--mod-table-summary-row-background-color, var(--spectrum-table-summary-row-background-color))); } /********* REGULAR / DEFAULT *********/ @@ -982,3 +799,87 @@ padding-block: var(--spectrum-table-thumbnail-inner-content-block-spacing); } } + +/********* TESTS DURING DEVELOPMENT *********/ +.spectrum-Table-cell { + border-radius: calc(var(--spectrum-table-border-radius) - var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness))); +} + +.spectrum-Table-cell.is-focused { + outline-width: var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness)); + outline-style: solid; + outline-color: var(--highcontrast-table-cell-focus-indicator-color, var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color)))); + outline-offset: calc(var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness)) - 2px); +} + +.spectrum-Table-cell.is-selected { + background-color: var(--spectrum-table-selected-row-background-color); +} + +.spectrum-Table-row.is-selected { + --mod-table-row-background-color: var(--spectrum-table-selected-row-background-color); +} + +.spectrum-Table-row.is-focused .spectrum-Table-cell:first-child { + --spectrum-table-focus-indicator-shadow: inset; + box-shadow: var(--spectrum-table-focus-indicator-shadow) var(--spectrum-side-focus-indicator) 0 0 0 var(--spectrum-table-focus-indicator-color); + outline: none; +} + +/********* HIGH CONTRAST *********/ +@media (forced-colors: active) { + .spectrum-Table { + --highcontrast-table-row-background-color: Canvas; + --highcontrast-table-row-text-color: CanvasText; + --highcontrast-table-divider-color: CanvasText; + --highcontrast-table-border-color: CanvasText; + --highcontrast-table-icon-color: CanvasText; + --highcontrast-table-icon-color-focus: Highlight; + + --highcontrast-table-selected-row-background-color: Highlight; + --highcontrast-table-selected-row-text-color: HighlightText; + + @supports (color: SelectedItem) { + --highcontrast-table-selected-row-background-color: SelectedItem; + --highcontrast-table-selected-row-text-color: SelectedItemText; + } + + --highcontrast-table-selected-row-background-color-focus: Highlight; + --highcontrast-table-selected-row-text-color-focus: HighlightText; + --highcontrast-table-row-background-color-hover: Highlight; + --highcontrast-table-row-text-color-hover: HighlightText; + + --highcontrast-table-section-header-text-color: Canvas; + --highcontrast-table-section-header-background-color: CanvasText; + + --highcontrast-table-focus-indicator-color: Highlight; + --highcontrast-table-transition-duration: 0; + } + + .spectrum-Table-cell { + /* Removes unstylable readability backplate. */ + forced-color-adjust: none; + } + + .spectrum-Table-row { + &:hover, + &:focus-visible, + &.is-focused { + .spectrum-Table-checkbox .spectrum-Checkbox-box::before { + outline: var(--highcontrast-table-row-text-color-hover) 1px solid; + } + } + } + + .spectrum-Table-row.is-selected, + .spectrum-Table-row.is-drop-target, + .spectrum-Table-body.is-drop-target .spectrum-Table-row { + /* Ensure negative offset outline contrasts on top of SelectedItem background. */ + --highcontrast-table-cell-focus-indicator-color: var(--highcontrast-table-selected-row-text-color); + --highcontrast-table-cell-focus-extra-offset: 1px; + + .spectrum-Table-checkbox .spectrum-Checkbox-box::before { + outline: var(--highcontrast-table-selected-row-text-color) 1px solid; + } + } +} From 5156ac0264a08616681d96a22b85d6868c89d63b Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Tue, 20 May 2025 16:43:02 -0400 Subject: [PATCH 04/52] refactor(table): revert table stories to previous approach - removes createRow and createCell functions - reimplements ExampleRowItems - adjusts numerical data story to show text alignment - removes sort icon story - adds table row focus/selected states --- components/table/stories/table.stories.js | 860 ++++++++++------------ 1 file changed, 406 insertions(+), 454 deletions(-) diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index a1d4d389d96..950cb73b76c 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -3,7 +3,7 @@ import { isEmphasized, isLoading, isQuiet } from "@spectrum-css/preview/types"; import metadata from "../dist/metadata.json"; import packageJson from "../package.json"; import { TableGroup } from "./table.test.js"; -import { createRow, Template } from "./template.js"; +import { Template } from "./template.js"; /** * A table is used to create a container for displaying information. It allows users to sort, compare, and take action on large amounts of data. @@ -46,26 +46,6 @@ export default { }, control: "boolean", }, - isSortable: { - name: "Sortable column", - description: "If a table column is sortable, the header cell displays a sort icon.", - table: { - type: { summary: "boolean" }, - category: "Component", - }, - control: "boolean", - }, - sortableIcon: { - name: "Sortable icon", - description: "The icon to use for the sortable column.", - table: { - type: { summary: "string" }, - category: "Component", - }, - options: ["Sort", "SortDown", "SortUp", "none"], - control: "select", - if: { arg: "isSortable", eq: true }, - }, selectionMode: { name: "Selection mode", description: "Determines whether items in the table can be selected, and if users can select only one or multiple items.", @@ -73,6 +53,7 @@ export default { table: { type: { summary: "string" }, category: "Selection", + disable: true, }, options: ["none", "single", "multiple"], control: "select", @@ -112,9 +93,23 @@ export default { isDropTarget: false, useScroller: false, hasColumnDividers: false, - isSortable: false, - sortableIcon: "", - rowItems: [], + rowItems: [ + { + cellContent: "Row item alpha", + }, + { + cellContent: "Row item bravo", + }, + { + cellContent: "Row item charlie", + }, + { + cellContent: "Row item delta", + }, + { + cellContent: "Row item echo", + }, + ], }, parameters: { design: { @@ -126,31 +121,47 @@ export default { }, }; + +const ExampleRowItems = [ + { + cellContent: ["Table row alpha", "Alpha", "Table row alpha"], + showCheckbox: true, + }, + { + cellContent: [ + "Selected row bravo. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + "Bravo", + "Table row bravo. Lorem ipsum dolor sit amet.", + ], + showCheckbox: true, + isSelected: true, + }, + { + cellContent: "Selected row charlie", + showCheckbox: true, + isSelected: true, + }, + { + cellContent: "Table row delta", + showCheckbox: true, + }, + { + cellContent: "Echo", + showCheckbox: true, + }, + { + cellContent: "Foxtrot", + showCheckbox: true, + }, +]; + /** * The default table also uses the regular density. Similar to a paragraph of text, textual data is always left-aligned within a table. Never use center alignment. + * + * Tables with sortable columns can show different states of sorting: unsorted, ascending, and descending. Additionally, tables can also trigger a menu, as indicated by the chevron. */ export const Default = TableGroup.bind({}); -Default.args = { - rowItems: [ - /* In createRow, the first argument is the TableRow props, and the second is the content of the cells in the row, and the third is options like thumbnails, sortable, etc. */ - createRow( - {isSectionHeader: true}, - ["Column 1", "Column 2", "Column 3"] - ), - createRow( - {isSummaryRow: true}, - ["Summary row", "Summary 2", "Summary 3"] - ), - createRow( - {}, - ["Table row alpha", "Column 2", "Column 3"] - ), - createRow( - {}, - ["Table row charlie", "Column 2", "Column 3"] - ), - ], -}; +Default.args = {}; // ********* DOCS ONLY ********* // /** @@ -175,7 +186,7 @@ Loading.parameters = { }; /** - * The compact variant decreases the spacing used within the table. + * The compact variant decreases the spacing used within the table rows, except for the header row. */ export const Compact = Template.bind({}); Compact.args = { @@ -189,7 +200,7 @@ Compact.parameters = { Compact.storyName = "Density - compact"; /** - * The spacious variant increases the spacing used within the table. + * The spacious variant increases the spacing used within the table rows, except for the header row. */ export const Spacious = Template.bind({}); Spacious.args = { @@ -209,10 +220,9 @@ Spacious.storyName = "Density - spacious"; export const MultiSelect = Template.bind({}); MultiSelect.storyName = "Selection mode: multiple"; MultiSelect.args = { - ...Default.args, + rowItems: ExampleRowItems, selectionMode: "multiple", }; -MultiSelect.tags = ["!dev"]; MultiSelect.parameters = { chromatic: { disableSnapshot: true }, }; @@ -223,10 +233,33 @@ MultiSelect.parameters = { export const SingleSelect = Template.bind({}); SingleSelect.storyName = "Selection mode: single"; SingleSelect.args = { - ...Default.args, selectionMode: "single", + rowItems: [ + { + cellContent: ["Pikachu", "Electric", "35"], + textAlignment: { + 2: "end" + }, + showCheckbox: true, + isSelected: true, + isChecked: true, + }, + { + cellContent: ["Charmander", "Fire", "39"], + textAlignment: { + 2: "end" + }, + showCheckbox: true, + }, + { + cellContent: ["Mew", "Psychic", "100"], + textAlignment: { + 2: "end" + }, + showCheckbox: true, + } + ], }; -SingleSelect.tags = ["!dev"]; SingleSelect.parameters = { chromatic: { disableSnapshot: true }, }; @@ -251,42 +284,24 @@ EmphasizedMultiSelect.parameters = { export const NumericalData = Template.bind({}); NumericalData.args = { rowItems: [ - createRow( - {isSectionHeader: true}, - ["Pokemon", "Type", "Health"], - { - textAlignment: { - 2: "end" - } + { + cellContent: ["Pikachu", "Electric", "35"], + textAlignment: { + 2: "end" } - ), - createRow( - {}, - ["Pikachu", "Electric", "35"], - { - textAlignment: { - 2: "end" - } - } - ), - createRow( - {}, - ["Charmander", "Fire", "39"], - { - textAlignment: { - 2: "end" - } + }, + { + cellContent: ["Charmander", "Fire", "39"], + textAlignment: { + 2: "end" } - ), - createRow( - {}, - ["Mew", "Psychic", "100"], - { - textAlignment: { - 2: "end" - } + }, + { + cellContent: ["Mew", "Psychic", "100"], + textAlignment: { + 2: "end" } - ), + } ], }; NumericalData.storyName = "Numerical data"; @@ -294,6 +309,43 @@ NumericalData.parameters = { chromatic: { disableSnapshot: true }, }; +/** + * The cells and rows within the table have different states based on selection and focus. + */ +export const TableStates = Template.bind({}); +TableStates.args = { + rowItems: [ + { + cellContent: "Focused selected row, no rounded corners", + isFocused: true, + isSelected: true, + }, + { + cellContent: "Table row bravo", + }, + { + cellContent: "Selected unfocused row, no rounded corners", + isSelected: true, + }, + { + cellContent: "Focused unselected row, no rounded corners", + isFocused: true, + }, + { + cellContent: "Table row echo", + }, + { + cellContent: "Focused selected row, with rounded corners", + isFocused: true, + isSelected: true, + } + ], +}; +TableStates.storyName = "Row and cell states"; +TableStates.parameters = { + chromatic: { disableSnapshot: true }, +}; + /** * Quiet tables are for when a table is meant to be supplementary, subtle, or lightweight. The quiet table utilizes the `.spectrum-Table--quiet` class and has a transparent background and no borders on the left and right. */ @@ -344,31 +396,23 @@ WithColumnDividers.parameters = { export const SummaryAndSelected = Template.bind({}); SummaryAndSelected.args = { rowItems: [ - createRow( - {isSectionHeader: true}, - ["Column 1", "Column 2", "Column 3"], - { - hasMenu: { - 0: true, - } - } - ), - createRow( - {}, - ["Table row alpha", "Column 2", "Column 3"] - ), - createRow( - {isSelected: true}, - ["Table row bravo (selected)", "Column 2", "Column 3"] - ), - createRow( - {}, - ["Table row charlie", "Column 2", "Column 3"] - ), - createRow( - {isSummaryRow: true}, - ["Summary row", "Summary 2", "Summary 3"] - ) + { + cellContent: "Table row alpha", + }, + { + cellContent: "Table row bravo", + }, + { + cellContent: "Selected row charlie", + isSelected: true, + }, + { + cellContent: "Table row delta", + }, + { + cellContent: "Summary row", + isSummaryRow: true, + }, ], }; SummaryAndSelected.storyName = "Summary and selected"; @@ -382,31 +426,29 @@ SummaryAndSelected.parameters = { export const SectionHeader = Template.bind({}); SectionHeader.args = { rowItems: [ - createRow( - {isSectionHeader: true}, - ["Section header", "Column 2", "Column 3"], - { - hasMenu: { - 1: true - } - } - ), - createRow( - {}, - ["Table row alpha", "Column 2", "Column 3"] - ), - createRow( - {}, - ["Table row bravo", "Column 2", "Column 3"] - ), - createRow( - {isSectionHeader: true}, - ["Another section header", "Column 2", "Column 3"] - ), - createRow( - {}, - ["Table row charlie", "Column 2", "Column 3"] - ) + { + cellContent: "Section header", + isSectionHeader: true, + }, + { + cellContent: "Table row alpha", + }, + { + cellContent: "Table row bravo", + }, + { + cellContent: "Table row charlie", + }, + { + cellContent: "Another section header", + isSectionHeader: true, + }, + { + cellContent: "Table row delta", + }, + { + cellContent: "Table row echo", + }, ], }; SectionHeader.storyName = "Section header"; @@ -437,39 +479,29 @@ export const Scrollable = Template.bind({}); Scrollable.args = { useScroller: true, rowItems: [ - createRow( - {isSectionHeader: true}, - ["Column 1", "Column 2", "Column 3"] - ), - createRow( - {}, - ["Table row alpha", "Column 2", "Column 3"] - ), - createRow( - {}, - ["Table row bravo", "Column 2", "Column 3"] - ), - createRow( - {isSelected: true}, - ["Table row charlie (selected)", "Column 2", "Column 3"] - ), - createRow( - {}, - ["Table row delta", "Column 2", "Column 3"] - ), - createRow( - {}, - ["Table row echo", "Column 2", "Column 3"] - ), - createRow( - {}, - ["Table row foxtrot", "Column 2", "Column 3"] - ), - createRow( - {isSummaryRow: true}, - ["Summary row", "Column 2", "Column 3"] - ) - + { + cellContent: "Table row alpha", + }, + { + cellContent: "Table row bravo", + }, + { + cellContent: "Table row charlie", + isSelected: true, + }, + { + cellContent: "Table row delta", + }, + { + cellContent: "Table row echo", + }, + { + cellContent: "Table row foxtrot", + }, + { + cellContent: "Summary row", + isSummaryRow: true, + }, ], }; Scrollable.tags = ["!dev"]; @@ -497,67 +529,60 @@ DivsScrollable.parameters = { export const Collapsible = Template.bind({}); Collapsible.args = { rowItems: [ - createRow( - {isSectionHeader: true}, - ["Collapsible options", "Column 2", "Column 3"] - ), - createRow( - { - isCollapsible: true, - isExpanded: true, - tier: 0, - ariaControls: "table-cr-bravo table-cr-delta", - id: "table-cr-alpha" - }, - ["Table row alpha", "Column 2", "Column 3"] - ), - createRow( - { - isCollapsible: true, - tier: 1, - ariaControls: "table-cr-charlie", - id: "table-cr-bravo" - }, - ["Table row bravo", "Column 2", "Column 3"] - ), - createRow( - { - isCollapsible: true, - isHidden: true, - tier: 2, - id: "table-cr-charlie" - }, - ["Table row charlie", "Column 2", "Column 3"] - ), - createRow( - { - isSelected: true, - isCollapsible: true, - isExpanded: true, - tier: 1, - ariaControls: "table-cr-echo table-cr-foxtrot", - id: "table-cr-delta" - }, - ["Table row delta", "Column 2", "Column 3"] - ), - createRow( - { - tier: 2, - isLastTier: true, - isCollapsible: true, - id: "table-cr-echo" - }, - ["Table row echo", "Column 2", "Column 3"] - ), - createRow( - { - tier: 2, - isLastTier: true, - isCollapsible: true, - id: "table-cr-foxtrot" - }, - ["Table row foxtrot", "Column 2", "Column 3"] - ) + { + cellContent: "Table row alpha", + isCollapsible: true, + isExpanded: true, + tier: 0, + ariaControls: "table-cr-bravo table-cr-delta", + id: "table-cr-alpha", + }, + { + cellContent: + "Table row bravo. There is actually another collapsed row here that's not visible. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", + isCollapsible: true, + tier: 1, + ariaControls: "table-cr-charlie", + id: "table-cr-bravo", + }, + { + cellContent: [ + "Table row charlie", + "Default not visible", + "Default not visible", + ], + isCollapsible: true, + isHidden: true, + tier: 2, + id: "table-cr-charlie", + }, + { + cellContent: "Selected row delta", + isSelected: true, + isCollapsible: true, + isExpanded: true, + tier: 1, + ariaControls: "table-cr-echo table-cr-foxtrot", + id: "table-cr-delta", + }, + { + cellContent: "Table row echo", + tier: 2, + isLastTier: true, + isCollapsible: true, + id: "table-cr-echo", + }, + { + cellContent: "Table row foxtrot", + tier: 2, + isLastTier: true, + isCollapsible: true, + id: "table-cr-foxtrot", + }, + { + cellContent: "Summary row", + isSummaryRow: true, + }, ], }; Collapsible.parameters = { @@ -569,65 +594,67 @@ CollapsibleMultiSelect.storyName = "Selection mode: multiple, collapsible rows"; CollapsibleMultiSelect.args = { selectionMode: "multiple", rowItems: [ - createRow( - {isSectionHeader: true}, - ["Collapsible options", "Column 2", "Column 3"] - ), - createRow( - { - isCollapsible: true, - isExpanded: true, - tier: 0, - ariaControls: "table-ms-bravo table-ms-charlie", - id: "table-ms-alpha" - }, - ["Table row alpha", "Column 2", "Column 3"] - ), - createRow( - { - isCollapsible: true, - tier: 1, - id: "table-ms-bravo" - }, - ["Table row bravo", "Column 2", "Column 3"] - ), - createRow( - { - isCollapsible: true, - isSelected: true, - tier: 1, - isExpanded: true, - ariaControls: "table-ms-delta table-ms-echo", - id: "table-ms-charlie" - }, - ["Table row charlie (selected)", "Column 2", "Column 3"] - ), - createRow( - { - tier: 2, - isLastTier: true, - isCollapsible: true, - id: "table-ms-delta" - }, - ["Table row delta", "Column 2", "Column 3"] - ), - createRow( - { - tier: 2, - isLastTier: true, - isCollapsible: true, - id: "table-ms-echo" - }, - ["Table row echo", "Column 2", "Column 3"] - ), - createRow( - { - isCollapsible: true, - tier: 0, - id: "table-ms-foxtrot" - }, - ["Table row foxtrot", "Column 2", "Column 3"] - ) + { + showCheckbox: true, + cellContent: "Table row alpha", + isCollapsible: true, + isExpanded: true, + tier: 0, + ariaControls: "table-cr-bravo table-cr-delta", + id: "table-cr-alpha", + }, + { + showCheckbox: true, + cellContent: + "Table row bravo. There is actually another collapsed row here that's not visible. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", + isCollapsible: true, + tier: 1, + ariaControls: "table-cr-charlie", + id: "table-cr-bravo", + }, + { + showCheckbox: true, + cellContent: [ + "Table row charlie", + "Default not visible", + "Default not visible", + ], + isCollapsible: true, + isHidden: true, + tier: 2, + id: "table-cr-charlie", + }, + { + showCheckbox: true, + cellContent: "Selected row delta", + isSelected: true, + isCollapsible: true, + isExpanded: true, + tier: 1, + ariaControls: "table-cr-echo table-cr-foxtrot", + id: "table-cr-delta", + }, + { + showCheckbox: true, + cellContent: "Table row echo", + tier: 2, + isLastTier: true, + isCollapsible: true, + id: "table-cr-echo", + }, + { + showCheckbox: true, + cellContent: "Table row foxtrot", + tier: 2, + isLastTier: true, + isCollapsible: true, + id: "table-cr-foxtrot", + }, + { + showCheckbox: true, + cellContent: "Summary row", + isSummaryRow: true, + }, ], }; CollapsibleMultiSelect.parameters = { @@ -640,44 +667,19 @@ CollapsibleMultiSelect.parameters = { export const Visuals = Template.bind({}); Visuals.args = { rowItems: [ - createRow( - {isSectionHeader: true}, - ["Avatar", "Icon", "Thumbnail"] - ), - createRow( - {}, - ["Avatar Example", "Icons", "Thumbnail Example"], - { - visualElements: { - 0: "avatar", - 1: "icon", - 2: "thumbnail" - } - } - ), - createRow( - {}, - ["Avatar Example", "Icons", "Thumbnail Example"], - { - visualElements: { - 0: "avatar", - 1: "icon", - 2: "thumbnail" - } - } - ), - createRow( - {}, - ["Avatar Example", "Icons", "Thumbnail Example"], - { - visualElements: { - 0: "avatar", - 1: "icon", - 2: "thumbnail" - } - } - ) - ] + { + cellContent: "Avatar example", + visualElement: "avatar", + }, + { + cellContent: "Icon example", + visualElement: "icon", + }, + { + cellContent: "Thumbnail example", + visualElement: "thumbnail", + }, + ], }; Visuals.parameters = { chromatic: { disableSnapshot: true }, @@ -689,113 +691,70 @@ Visuals.parameters = { export const VisualsCollapsible = Template.bind({}); VisualsCollapsible.args = { rowItems: [ - createRow( - {isSectionHeader: true}, - ["Visual options", "Column 2", "Column 3"] - ), - createRow( - { - isCollapsible: true, - isExpanded: true, - tier: 0, - ariaControls: "table-vc-bravo table-vc-charlie", - id: "table-vc-alpha" - }, - ["Visual row alpha", "Column 2", "Column 3"], - { - visualElements: { - 0: "avatar", - 1: "icon", - 2: "thumbnail" - } - } - ), - createRow( - { - isCollapsible: true, - tier: 1, - id: "table-vc-bravo" - }, - ["Visual row bravo", "Column 2", "Column 3"], - { - visualElements: { - 0: "icon", - 1: "avatar", - 2: "thumbnail" - } - } - ), - createRow( - { - isCollapsible: true, - isExpanded: true, - tier: 1, - ariaControls: "table-vc-delta", - id: "table-vc-charlie" - }, - ["Visual row charlie", "Column 2", "Column 3"], - { - visualElements: { - 0: "thumbnail", - 1: "avatar", - 2: "icon" - } - } - ), - createRow( - { - tier: 2, - isLastTier: true, - isCollapsible: true, - id: "table-vc-delta" - }, - ["Visual row delta", "Column 2", "Column 3"], - { - visualElements: { - 0: "avatar", - 1: "icon", - 2: "thumbnail" - } - } - ) - ] + { + cellContent: "Table row alpha", + isCollapsible: true, + isExpanded: true, + tier: 0, + ariaControls: "table-cr-bravo table-cr-delta", + id: "table-cr-alpha", + }, + { + cellContent: + "Table row bravo. There is actually another collapsed row here that's not visible. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", + isCollapsible: true, + tier: 1, + ariaControls: "table-cr-charlie", + id: "table-cr-bravo", + visualElement: "avatar", + }, + { + cellContent: [ + "Table row charlie", + "Default not visible", + "Default not visible", + ], + isCollapsible: true, + isHidden: true, + tier: 2, + id: "table-cr-charlie", + }, + { + cellContent: "Selected row delta", + isSelected: true, + isCollapsible: true, + isExpanded: true, + tier: 1, + ariaControls: "table-cr-echo table-cr-foxtrot", + id: "table-cr-delta", + visualElement: "icon", + }, + { + cellContent: "Table row echo", + tier: 2, + isLastTier: true, + isCollapsible: true, + id: "table-cr-echo", + visualElement: "thumbnail", + }, + { + cellContent: "Table row foxtrot", + tier: 2, + isLastTier: true, + isCollapsible: true, + id: "table-cr-foxtrot", + }, + { + cellContent: "Summary row", + isSummaryRow: true, + }, + ], }; VisualsCollapsible.storyName = "Visuals: collapsible"; VisualsCollapsible.parameters = { chromatic: { disableSnapshot: true }, }; -/** - * Tables with sortable columns can show different states of sorting: unsorted, ascending, and descending. - */ -export const SortIcons = Template.bind({}); -SortIcons.args = { - rowItems: [ - createRow( - { isSectionHeader: true }, - ["Default sort", "Ascending sort", "Descending sort"], - { - sortableColumns: [0, 1, 2], - sortableIconNames: { - 0: "Sort", - 1: "SortUp", - 2: "SortDown", - } - } - ), - createRow({}, ["Data A", "Data B", "Data C"]), - createRow({}, ["Data D", "Data E", "Data F"]), - createRow({}, ["Data G", "Data H", "Data I"]), - ], - isSortable: true, -}; -SortIcons.tags = ["!dev"]; -SortIcons.storyName = "Sort icons"; -SortIcons.parameters = { - chromatic: { disableSnapshot: true }, -}; - - // TODO: The design team doesn't have dropzones in the table component, so they are removed from the docs page for now. /** * The table body can accept dropped content. @@ -817,34 +776,27 @@ BodyDropZone.parameters = { export const RowDropZone = Template.bind({}); RowDropZone.args = { rowItems: [ - createRow( - {isSectionHeader: true}, - ["Column 1", "Column 2", "Column 3"] - ), - createRow( - {isDropTarget: true}, - ["Table row alpha", "Column 2", "Column 3"] - ), - createRow( - {}, - ["Table row bravo", "Column 2", "Column 3"] - ), - createRow( - {isDropTarget: true}, - ["Table row charlie", "Column 2", "Column 3"] - ), - createRow( - {}, - ["Table row delta", "Column 2", "Column 3"] - ), - createRow( - {}, - ["Table row echo", "Column 2", "Column 3"] - ), - createRow( - {isDropTarget: true}, - ["Table row foxtrot", "Column 2", "Column 3"] - ) + { + cellContent: "Table row alpha", + isDropTarget: true, + }, + { + cellContent: "Table row bravo", + }, + { + cellContent: "Table row charlie", + isDropTarget: true, + }, + { + cellContent: "Table row delta", + }, + { + cellContent: "Table row echo", + }, + { + cellContent: "Table row foxtrot", + isDropTarget: true, + }, ], }; RowDropZone.tags = ["!autodocs","!dev"]; From bdea751268223dc7b83f99faef08b7f39d219a85 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Tue, 20 May 2025 17:10:30 -0400 Subject: [PATCH 05/52] refactor(table): revert template to previous approach - removes createRow and createCell functions - removes the TableCell and TableRow components - adds support for text alignment, selection and checkboxes, loading and empty states - reimplements sorting icons with new S2 icons in spec - attempts to re-simplify the template --- components/table/stories/template.js | 512 +++++++++++---------------- 1 file changed, 210 insertions(+), 302 deletions(-) diff --git a/components/table/stories/template.js b/components/table/stories/template.js index e04461a6ea7..ee662aa2295 100644 --- a/components/table/stories/template.js +++ b/components/table/stories/template.js @@ -3,7 +3,7 @@ import { Template as Button } from "@spectrum-css/button/stories/template.js"; import { Template as Checkbox } from "@spectrum-css/checkbox/stories/template.js"; import { Template as Icon } from "@spectrum-css/icon/stories/template.js"; import { Template as IllustratedMessage } from "@spectrum-css/illustratedmessage/stories/template.js"; -import { getRandomId, renderContent } from "@spectrum-css/preview/decorators"; +import { getRandomId } from "@spectrum-css/preview/decorators"; import { Template as ProgressCircle } from "@spectrum-css/progresscircle/stories/template.js"; import { Template as Thumbnail } from "@spectrum-css/thumbnail/stories/template.js"; import { classMap } from "lit/directives/class-map.js"; @@ -13,202 +13,88 @@ import { html, literal } from "lit/static-html.js"; import "../index.css"; -/* TableCellTemplate renders a table cell as a div/td/th. */ -export const TableCellTemplate = ({ - rootClass = "spectrum-Table-cell", - cellContent = "", - visualElement, - isFocused = false, - isSelected = false, - isHeaderCell = false, - hasColumnDividers = false, - textAlignment = "start", - hasMenu = false, - useDivs = false, - role, - customClasses = [], - isCollapsible = false, - isLastTier = false, - isExpanded = false, - isSortable = false, - sortableIcon, - ariaControls, -} = {}, context = {}) => { - - const useThumbnail = visualElement === "thumbnail"; - const useAvatar = visualElement === "avatar"; - const useIcon = visualElement === "icon"; - - // Use Table tags or Div tags. - // Note: Lit must use the 'literal' function for dynamic tags to work. - const cellTag = useDivs ? literal`div` : isHeaderCell ? literal`th` : literal`td`; - - // Content for a table cell. - const getCellContent = (columnIndex) => { - const content = Array.isArray(cellContent) - ? cellContent[columnIndex] - : cellContent; - - // Only render visuals in the first two columns - if (columnIndex < 2 && visualElement) { - // options/classes for visual elements - const visuals = { - thumbnail: { - component: Thumbnail, - props: { - size: "75", - imageURL: "example-card-landscape.png", - isCover: true, - }, - containerClass: "spectrum-Table-thumbnailInner", - contentClass: "spectrum-Table-thumbnailContent" - }, - avatar: { - component: Avatar, - props: { - size: "75", - }, - containerClass: "spectrum-Table-avatarInner", - contentClass: "spectrum-Table-avatarContent" - }, - icon: { - component: Icon, - props: { - iconName: "Image", - iconSet: "workflow", - }, - containerClass: "spectrum-Table-iconInner", - contentClass: "spectrum-Table-iconContent" - } - }; - - // Get configuration for the current visual element - const config = visuals[visualElement]; - - if (config) { - return html` -
- ${config.component(config.props, context)} -
${content}
-
- `; - } - } - - // Default case - just return the content - return content; - }; - - // The content and classes depend on whether this is a collapsible cell - const cellClasses = { - [`${rootClass}`]: true, - [`${rootClass}--header`]: isHeaderCell ? true : undefined, - [`${rootClass}--thumbnail`]: useThumbnail, - [`${rootClass}--avatar`]: useAvatar, - [`${rootClass}--icon`]: useIcon, - [`${rootClass}--collapsible`]: isCollapsible, - ["is-sortable"]: isSortable && isHeaderCell, - ["is-focused"]: isFocused, - ["is-selected"]: isSelected, - [`${rootClass}--alignEnd`]: textAlignment === "end", - [`${rootClass}--divider`]: hasColumnDividers, - ...customClasses.reduce((a, c) => ({ ...a, [c]: true }), {}), - }; - - const cellContentHtml = isCollapsible - ? html` -
- ${!isLastTier ? Button({ - size: "m", - iconName: "ChevronRight100", - iconSet: "ui", - hideLabel: true, - customClasses: ["spectrum-Table-disclosureIcon", "spectrum-Button--iconOnly"], - ariaExpanded: isExpanded, - ariaControls, - }, context) : null} -
${getCellContent(1)}
-
- ` - : isHeaderCell && isSortable - ? html` -
- ${Icon({ - iconName: sortableIcon === "none" ? undefined : sortableIcon, - iconSet: "workflow", - customClasses: ["spectrum-Table-icon--sort"], - }, context)} - ${getCellContent(1)} -
- ` - : getCellContent(1); - - return html` - <${cellTag} - class=${classMap(cellClasses)} - role=${ifDefined(useDivs ? "cell" : role ? role : undefined)} - > - ${cellContentHtml} - ${when(hasMenu, () => html` - ${Button({ - size: "m", - iconName: "ChevronDown100", - iconSet: "ui", - hideLabel: true, - customClasses: ["spectrum-Table-disclosureIcon", "spectrum-Button--iconOnly"], - ariaExpanded: isExpanded, - ariaControls, - }, context)} - `)} - - `; -}; - -// TableRowTemplate is used to render a table row as a div/tr. -export const TableRowTemplate = ({ - rootClass = "spectrum-Table-row", - rowContent = [], +export const TableRowItem = ({ + rootClass = "spectrum-Table", + cellContent = "Row Item Text", showCheckbox = false, isSelected = false, - isFocused = false, isSummaryRow = false, isSectionHeader = false, + isEmphasized = true, isCollapsible = false, isExpanded = false, - isSortable = false, - sortableIcon, - isEmphasized = false, isHidden = false, - textAlignment = "start", - selectionMode = "none", hasColumnDividers = false, tier, isLastTier = false, useDivs = false, + visualElement, + textAlignment, isDropTarget = false, ariaControls, customClasses = [], } = {}, context = {}) => { + const useVisuals = visualElement !== undefined && !isSummaryRow && !isSectionHeader; + const useColumnDividers = hasColumnDividers && !isSummaryRow && !isSectionHeader; // Use Table tags or Div tags. // Note: Lit must use the 'literal' function for dynamic tags to work. const rowTag = useDivs ? literal`div` : literal`tr`; + const cellTag = useDivs ? literal`div` : literal`td`; - // For collapsible rows, we need to pass special props to the first cell - // to render the disclosure button within that cell - const shouldRenderCollapsible = isCollapsible && !isSummaryRow && !isSectionHeader; + // Content for a table cell. + const getCellContent = (columnIndex) => { + const content = Array.isArray(cellContent) + ? cellContent[columnIndex] + : cellContent; + + if (useVisuals && columnIndex < 2) { + return html` +
+ ${visualElement === "thumbnail" ? + Thumbnail({ + size: "75", + imageURL: "example-card-landscape.png", + isCover: true, + }, context) + : visualElement === "avatar" ? + Avatar({ + size: "75", + imageURL: "example-card-landscape.png", + isCover: true, + }, context) + : visualElement === "icon" ? + Icon({ + iconName: "Image", + setName: "workflow", + }, context) + : null} +
${content}
+
+ `; + } + else { + return content; + } + }; + + // For each column, apply the text alignment specified in textAlignment + const getTextAlignment = (columnIndex) => { + if (!textAlignment) return "start"; + + return textAlignment[columnIndex] || "start"; + }; return html` <${rowTag} class=${classMap({ - [`${rootClass}`]: true, - [`${rootClass}--summary`]: isSummaryRow, - [`${rootClass}--sectionHeader`]: isSectionHeader, - [`${rootClass}--collapsible`]: isCollapsible, - ["is-focused"]: isFocused, + [`${rootClass}-row`]: true, + [`${rootClass}-row--summary`]: isSummaryRow, + [`${rootClass}-row--sectionHeader`]: isSectionHeader, + [`${rootClass}-row--collapsible`]: isCollapsible, + [`${rootClass}-cell--divider`]: useColumnDividers, ["is-selected"]: isSelected, - ["is-sortable"]: isSortable && isSectionHeader, - ["is-emphasized"]: isEmphasized, + ["is-expanded"]: isExpanded, ["is-last-tier"]: isLastTier, ["is-drop-target"]: isDropTarget, ...customClasses.reduce((a, c) => ({ ...a, [c]: true }), {}), @@ -218,123 +104,100 @@ export const TableRowTemplate = ({ data-tier=${ifDefined(tier)} ?hidden=${isHidden} > - ${when(showCheckbox, () => html` - ${TableCellTemplate({ - useDivs, - hasColumnDividers, - isSortable, - sortableIcon, - textAlignment, - role: "gridcell", - customClasses: [`${rootClass}-checkboxCell`], - cellContent: - Checkbox({ - size: "m", - isFocused, - isChecked: isSelected, - isEmphasized, - isIndeterminate: selectionMode === "multiple" && isSectionHeader ? true : undefined, - customClasses: [`${rootClass}-checkbox`], - }, context) - })}` - )} - - ${when(isSectionHeader, () => html` - ${renderContent(rowContent, { - args: { - useDivs, - hasColumnDividers, - isHeaderCell: true, - isSortable, - sortableIcon, - textAlignment, - }, - })} - `)} - - ${when(!isSectionHeader, () => html` - ${renderContent(rowContent, { - args: { - useDivs, - hasColumnDividers, - isSortable, - sortableIcon, - textAlignment, - // Only pass collapsible props to the first cell - isCollapsible: shouldRenderCollapsible, - isLastTier: shouldRenderCollapsible ? isLastTier : undefined, - isExpanded: shouldRenderCollapsible ? isExpanded : undefined, - ariaControls: shouldRenderCollapsible ? ariaControls : undefined, - }, - })} - `)} + ${when(showCheckbox && !isSectionHeader, () => html` + <${cellTag} + role="gridcell" + class=${classMap({ + [`${rootClass}-cell`]: true, + [`${rootClass}-checkboxCell`]: true, + [`${rootClass}-cell--alignEnd`]: getTextAlignment(0) === "end", + })} + > + ${when(!isSummaryRow, () => + Checkbox({ + size: "m", + isEmphasized, + isChecked: isSelected, + customClasses: [`${rootClass}-checkbox`], + }, context) + )} + ` + )} + + ${isCollapsible + ? html` + <${cellTag} + role=${ifDefined(showCheckbox ? "gridcell" : useDivs ? "cell" : undefined)} + class=${classMap({ + [`${rootClass}-cell`]: true, + [`${rootClass}-cell--collapsible`]: true, + [`${rootClass}-cell--visual`]: useVisuals, + [`${rootClass}-cell--divider`]: useColumnDividers, + [`${rootClass}-cell--alignEnd`]: getTextAlignment(0) === "end", + })} + > +
+ ${when(!isLastTier, () => + Button({ + size: "m", + iconName: "ChevronRight100", + iconSet: "ui", + hideLabel: true, + customClasses: [`${rootClass}-disclosureIcon`], + ariaExpanded: isExpanded, + ariaControls, + }, context) + )} + ${useVisuals ? getCellContent(0) : html`
${getCellContent(0)}
`} +
+ ` + : html` + <${cellTag} + role=${ifDefined(showCheckbox ? "gridcell" : useDivs ? "cell" : undefined)} + class=${classMap({ + [`${rootClass}-cell`]: true, + [`${rootClass}-cell--visual`]: useVisuals, + [`${rootClass}-cell--divider`]: useColumnDividers, + [`${rootClass}-cell--alignEnd`]: getTextAlignment(0) === "end", + })} + colspan=${ifDefined(isSectionHeader && showCheckbox ? "4" : isSectionHeader ? "3" : undefined)} + >${getCellContent(0)}` + } + ${when(!isSectionHeader, () => html` + <${cellTag} + role=${ifDefined(showCheckbox ? "gridcell" : useDivs ? "cell" : undefined)} + class=${classMap({ + [`${rootClass}-cell`]: true, + [`${rootClass}-cell--visual`]: useVisuals, + [`${rootClass}-cell--divider`]: useColumnDividers, + [`${rootClass}-cell--alignEnd`]: getTextAlignment(1) === "end", + })} + >${getCellContent(1)} + <${cellTag} + role=${ifDefined(showCheckbox ? "gridcell" : useDivs ? "cell" : undefined)} + class=${classMap({ + [`${rootClass}-cell`]: true, + [`${rootClass}-cell--divider`]: useColumnDividers, + [`${rootClass}-cell--alignEnd`]: getTextAlignment(2) === "end", + })} + >${getCellContent(2)}` + )} `; }; -/* Create a table cell */ -export const createCells = (cells, visualElements = {}, textAlignment = {}, hasMenu = {}) => { - return cells.map((content, index) => { - return (passthroughs, context) => TableCellTemplate({ - ...passthroughs, - cellContent: content, - visualElement: visualElements[index], - textAlignment: textAlignment[index], - hasMenu: hasMenu[index], - }, context); - }); -}; - -/* Create a table row - can be a regular row or header row with sortable columns */ -/* The first row is `thead`, and each object in this array can accept TableRowTemplate props. */ -/* In createRow, the first argument is the TableRow props, and the second is the content of the cells in the row, and the third is options like thumbnails, sortable, etc. */ -export const createRow = (rowProps = {}, cellsData = [], options = {}) => { - const { visualElements = {}, sortableColumns = [], sortableIconNames = {}, textAlignment = {}, hasMenu = {} } = options; - - // If it's a section header row with sortable columns - if (rowProps.isSectionHeader && sortableColumns.length > 0) { - const headerCellContent = cellsData.map((content, index) => { - return (passthroughs, context) => TableCellTemplate({ - ...passthroughs, - cellContent: content, - isHeaderCell: true, - visualElement: visualElements[index], - textAlignment: textAlignment[index], - hasMenu: hasMenu[index], - isSortable: sortableColumns.includes(index), - // Use the icon specified for this column index, or fall back to the default passed from passthroughs - sortableIcon: sortableColumns.includes(index) && sortableIconNames[index] - ? sortableIconNames[index] - : passthroughs.sortableIcon, - }, context); - }); - - return { - ...rowProps, - rowContent: headerCellContent, - }; - } - - return { - ...rowProps, - rowContent: createCells(cellsData, visualElements, textAlignment, hasMenu), - }; -}; - -// Table renders a table as a div/table. export const Template = ({ rootClass = "spectrum-Table", density = "standard", isQuiet = false, - isEmphasized = false, + isEmphasized = true, + isLoading = false, useDivs = false, - isSortable = false, - sortableIcon, - useScroller = false, selectionMode = "none", - isLoading = false, + useScroller = false, + visualElement = {}, isDropTarget = false, hasColumnDividers = false, rowItems = [], @@ -342,7 +205,7 @@ export const Template = ({ id = getRandomId("table"), } = {}, context = {}) => { - // empty state table + // Empty state if (!rowItems || !rowItems.length) return html` ${IllustratedMessage({ illustration: "NoData", @@ -377,13 +240,11 @@ export const Template = ({ const tableTag = useDivs ? literal`div` : literal`table`; const theadTag = useDivs ? literal`div` : literal`thead`; const tbodyTag = useDivs ? literal`div` : literal`tbody`; - - // If it's a section header, use the first item as thead content - const firstRowContent = rowItems && rowItems.length > 0 ? rowItems[0] : null; - const remainingRowContent = rowItems && rowItems.length > 1 ? rowItems.slice(1) : []; + const rowTag = useDivs ? literal`div` : literal`tr`; + const thTag = useDivs ? literal`div` : literal`th`; const rootClassMapVariants = { - [`${rootClass}--${density}`]: density !== "regular", + [`${rootClass}--${density}`]: density !== "standard", [`${rootClass}--quiet`]: isQuiet, [`${rootClass}--emphasized`]: isEmphasized, ...customClasses.reduce((a, c) => ({ ...a, [c]: true }), {}), @@ -407,18 +268,68 @@ export const Template = ({ class="${rootClass}-head" role=${ifDefined(useDivs ? "rowgroup" : undefined)} > - ${TableRowTemplate({ - useDivs, - hasColumnDividers, - isEmphasized, - isQuiet, - isSortable, - sortableIcon, - selectionMode, - showCheckbox: selectionMode !== "none", - // First row is the header row. - ...firstRowContent, - })} + <${rowTag} + role=${ifDefined(useDivs ? "row" : undefined)} + > + ${when(useCheckboxCell, () => html` + <${thTag} + class="spectrum-Table-headCell spectrum-Table-checkboxCell" + role=${ifDefined(useDivs ? "columnheader" : undefined)} + > + ${Checkbox({ + size: "m", + isEmphasized: isEmphasized, + isChecked: false, + isIndeterminate: selectionMode === "multiple" ? true : undefined, + customClasses: [`${rootClass}-checkbox`], + }, context)} + ` + )} + <${thTag} + class="${rootClass}-headCell is-sortable" + role=${ifDefined(useDivs ? "columnheader" : undefined)} + aria-sort="none" + tabindex="0" + > + ${Icon({ + iconName: "Sort", + setName: "workflow", + customClasses: [`${rootClass}-sortIcon`], + }, context)} + Column title + + <${thTag} + class="${rootClass}-headCell is-sortable is-sorted-desc" + role=${ifDefined(useDivs ? "columnheader" : undefined)} + aria-sort="descending" + tabindex="0" + > + ${Icon({ + iconName: "SortDown", + setName: "workflow", + customClasses: [`${rootClass}-sortIcon`], + }, context)} + Column title + ${Icon({ + iconName: "ChevronDown100", + setName: "ui", + customClasses: [`${rootClass}-menuIcon`], + }, context)} + + <${thTag} + class="${rootClass}-headCell is-sortable is-sorted-asc" + role=${ifDefined(useDivs ? "columnheader" : undefined)} + aria-sort="ascending" + tabindex="0" + > + ${Icon({ + iconName: "SortUp", + setName: "workflow", + customClasses: [`${rootClass}-sortIcon`], + }, context)} + Column title + + <${tbodyTag} class=${classMap({ @@ -427,16 +338,13 @@ export const Template = ({ })} role=${ifDefined(useDivs ? "rowgroup" : undefined)} > - ${remainingRowContent.map((item) => - TableRowTemplate({ + ${rowItems.map((item) => + TableRowItem({ + rootClass, useDivs, + visualElement, hasColumnDividers, isEmphasized, - isQuiet, - selectionMode, - isSortable, - sortableIcon, - showCheckbox: selectionMode !== "none", ...item, }, context) )} From db3aa6ccf2ca07d84ca4999187a303a784213e7e Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Tue, 20 May 2025 18:06:16 -0400 Subject: [PATCH 06/52] feat(table): add styles for headCells and icons - continues to reorganize custom properties - adds styles for headCells and icons --- components/table/index.css | 162 ++++++++++++------------------------- 1 file changed, 53 insertions(+), 109 deletions(-) diff --git a/components/table/index.css b/components/table/index.css index fe7a27657b5..de6567b2029 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -234,99 +234,80 @@ display: table; } -.spectrum-Table-sortedIcon { - display: none; - vertical-align: baseline; - margin-inline-start: var(--mod-table-sort-icon-inline-start-spacing, 0); - margin-inline-end: var(--mod-table-sort-icon-inline-end-spacing, var(--mod-table-icon-to-text, var(--spectrum-table-icon-to-text))); - transition: transform var(--highcontrast-table-transition-duration, var(--mod-table-transition-duration, var(--spectrum-table-transition-duration))) ease-in-out; -} - -.spectrum-Table-menuIcon { - vertical-align: middle; - margin-inline-start: var(--mod-table-icon-to-text, var(--spectrum-table-icon-to-text)); -} - -.spectrum-Table-headCell { - --spectrum-table-icon-color: var(--highcontrast-table-icon-color, var(--mod-table-icon-color-default, var(--spectrum-table-icon-color-default))); - - box-sizing: border-box; - text-align: start; - vertical-align: var(--mod-table-header-vertical-align, var(--spectrum-table-header-vertical-align)); - - font-family: var(--mod-table-header-font-family, var(--spectrum-table-row-font-family)); - font-size: var(--mod-table-header-font-size, var(--spectrum-table-row-font-size)); - font-weight: var(--mod-table-header-font-weight, var(--spectrum-table-header-font-weight)); - line-height: var(--mod-table-header-line-height, var(--spectrum-table-row-line-height)); - text-transform: var(--mod-table-header-text-transform, none); - - /* block-size functions as min-block-size when using display table-cell. */ - block-size: var(--mod-table-min-header-height, var(--spectrum-table-min-header-height)); - - padding-block: var(--mod-table-header-top-to-text, var(--spectrum-table-header-top-to-text)) var(--mod-table-header-bottom-to-text, var(--spectrum-table-header-bottom-to-text)); - padding-inline: var(--mod-table-cell-inline-space, var(--spectrum-table-cell-inline-space)); - - color: var(--mod-table-header-text-color, var(--spectrum-table-header-text-color)); - background-color: var(--mod-table-header-background-color, var(--spectrum-table-header-background-color)); - - border-radius: 0; - transition: color var(--highcontrast-table-transition-duration, var(--mod-table-transition-duration, var(--spectrum-table-transition-duration))) ease-in-out; - cursor: var(--mod-table-cursor-header-default, initial); - outline: 0; +/********* HEAD CELLS *********/ +.spectrum-Table-headRow { + .spectrum-Table-headCell { + box-sizing: border-box; + text-align: start; + vertical-align: var(--mod-table-header-vertical-align, var(--spectrum-table-header-vertical-align)); - .spectrum-Table-sortedIcon, - .spectrum-Table-menuIcon { - color: var(--spectrum-table-icon-color); - } + font-family: var(--mod-table-header-font-family, var(--spectrum-table-row-font-family)); + font-size: var(--mod-table-header-font-size, var(--spectrum-table-row-font-size)); + font-weight: var(--mod-table-header-font-weight, var(--spectrum-table-header-font-weight)); + line-height: var(--mod-table-header-line-height, var(--spectrum-table-row-line-height)); + text-transform: var(--mod-table-header-text-transform, none); - &.is-sortable { - cursor: var(--mod-table-cursor-header-sortable, pointer); + /* block-size functions as min-block-size when using display table-cell. */ + block-size: var(--mod-table-min-header-height, var(--spectrum-table-min-header-row-height)); - &:hover { - --spectrum-table-icon-color: var(--highcontrast-table-icon-color-focus, var(--mod-table-icon-color-hover, var(--spectrum-table-icon-color-hover))); - } + padding-block: var(--mod-table-header-row-top-to-text, var(--spectrum-table-header-row-top-to-text)) var(--mod-table-header-row-bottom-to-text, var(--spectrum-table-header-row-bottom-to-text)); + padding-inline: var(--mod-table-cell-inline-space, var(--spectrum-table-cell-inline-space)); - &:active { - --spectrum-table-icon-color: var(--highcontrast-table-icon-color-focus, var(--mod-table-icon-color-active, var(--spectrum-table-icon-color-active))); - } + color: var(--mod-table-header-text-color, var(--spectrum-table-header-text-color)); + background-color: var(--mod-table-header-background-color, var(--spectrum-table-header-background-color)); + border-block-start: var(--mod-table-border-width, var(--spectrum-table-border-width)) solid var(--highcontrast-table-border-color, var(--mod-table-border-color, var(--spectrum-table-border-color))); - &:focus { - --spectrum-table-icon-color: var(--highcontrast-table-icon-color-focus, var(--mod-table-icon-color-focus, var(--spectrum-table-icon-color-focus))); - } + transition: color var(--highcontrast-table-transition-duration, var(--mod-table-transition-duration, var(--spectrum-table-transition-duration))) ease-in-out; + cursor: var(--mod-table-cursor-header-default, initial); + outline: 0; - &:focus:hover { - --spectrum-table-icon-color: var(--highcontrast-table-icon-color-focus, var(--mod-table-icon-color-focus-hover, var(--spectrum-table-icon-color-focus-hover))); + &.is-sortable { + cursor: var(--mod-table-cursor-header-sortable, pointer); } + } - &:focus-visible, - &.is-keyboardFocused { - --spectrum-table-icon-color: var(--highcontrast-table-icon-color-focus, var(--mod-table-icon-color-key-focus, var(--spectrum-table-icon-color-key-focus))); - } + &:first-child .spectrum-Table-headCell:first-child { + border-start-start-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); + border-inline-start: var(--mod-table-border-width, var(--spectrum-table-border-width)) solid var(--highcontrast-table-border-color, var(--mod-table-border-color, var(--spectrum-table-border-color))); } - &.is-sorted-asc, - &.is-sorted-desc { - .spectrum-Table-sortedIcon { - display: inline-block; - } + &:last-child .spectrum-Table-headCell:last-child { + border-start-end-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); + border-inline-end: var(--mod-table-border-width, var(--spectrum-table-border-width)) solid var(--highcontrast-table-border-color, var(--mod-table-border-color, var(--spectrum-table-border-color))); } +} - &.is-sorted-asc { - .spectrum-Table-sortedIcon { - transform: rotateZ(-90deg); - } +.spectrum-Table-sortIcon { + &:last-child .spectrum-Table-headCell { + border-start-end-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); } } +/* Head cell column text */ .spectrum-Table-columnTitle { display: inline-block; } -.spectrum-Table-cell--alignCenter { - text-align: center; +/********* ICONS- SORT, *********/ +.spectrum-Table-sortIcon { + vertical-align: baseline; + margin-inline-start: var(--mod-table-sort-icon-inline-start-spacing, 0); + margin-inline-end: var(--mod-table-sort-icon-inline-end-spacing, var(--mod-table-icon-to-text, var(--spectrum-table-icon-to-text))); + transition: transform var(--highcontrast-table-transition-duration, var(--mod-table-transition-duration, var(--spectrum-table-transition-duration))) ease-in-out; + color: var(--spectrum-table-icon-color); + display: inline-block; +} + +.spectrum-Table-menuIcon { + vertical-align: middle; + margin-inline-start: var(--mod-table-icon-to-text, var(--spectrum-table-icon-to-text)); +} + +.spectrum-Table-cell--alignStart { + text-align: start; } -.spectrum-Table-cell--alignRight { +.spectrum-Table-cell--alignEnd { text-align: end; } @@ -429,41 +410,6 @@ outline: 0; } - &:hover, - &:focus-visible, - &.is-focused { - --highcontrast-table-row-text-color: var(--highcontrast-table-row-text-color-hover); - --highcontrast-table-icon-color: var(--highcontrast-table-row-text-color-hover); - --spectrum-table-cell-background-color: var(--highcontrast-table-row-background-color-hover, var(--mod-table-row-background-color-hover, var(--spectrum-table-row-background-color-hover))); - } - - &:active { - --highcontrast-table-row-text-color: var(--highcontrast-table-row-text-color-hover); - --highcontrast-table-icon-color: var(--highcontrast-table-row-text-color-hover); - --spectrum-table-cell-background-color: var(--highcontrast-table-row-background-color-hover, var(--mod-table-row-active-color, var(--spectrum-table-row-active-color))); - } - - &.is-selected { - --highcontrast-table-row-text-color: var(--highcontrast-table-selected-row-text-color); - --highcontrast-table-icon-color: var(--highcontrast-table-selected-row-text-color); - --spectrum-table-cell-background-color: var(--highcontrast-table-selected-row-background-color, var(--spectrum-table-selected-cell-background-color)); - - &:hover, - &:focus-visible, - &.is-focused { - --highcontrast-table-row-text-color: var(--highcontrast-table-selected-row-text-color-focus); - --highcontrast-table-icon-color: var(--highcontrast-table-selected-row-text-color-focus); - --spectrum-table-cell-background-color: var(--highcontrast-table-selected-row-background-color-focus, var(--spectrum-table-selected-cell-background-color-focus)); - } - } - - &.is-drop-target, - .spectrum-Table-body.is-drop-target & { - --highcontrast-table-row-text-color: var(--highcontrast-table-selected-row-text-color); - --highcontrast-table-icon-color: var(--highcontrast-table-selected-row-text-color); - --spectrum-table-cell-background-color: var(--highcontrast-table-selected-row-background-color, var(--mod-table-drop-zone-background-color, var(--spectrum-table-drop-zone-background-color))); - } - &.is-drop-target { /* Make sure negative offset outline is not covered by borders. */ --mod-table-border-color: var(--highcontrast-table-focus-indicator-color, transparent); @@ -707,8 +653,6 @@ } .spectrum-Table-disclosureIcon.spectrum-Table-disclosureIcon { - --spectrum-table-icon-color: var(--highcontrast-table-icon-color, var(--mod-table-icon-color-default, var(--spectrum-table-icon-color-default))); - block-size: var(--mod-table-disclosure-icon-size, var(--spectrum-table-disclosure-icon-size)); flex-basis: var(--mod-table-disclosure-icon-size, var(--spectrum-table-disclosure-icon-size)); flex-grow: 0; From 5e90ed1a88945ee0909ded55be164b146a6ee5a8 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Tue, 20 May 2025 20:27:36 -0400 Subject: [PATCH 07/52] feat(table): styles for body/rows/cells borders/focus/dropzones - clarifies some code comments - reorganizes code for focus styles - corrects border and border-radii for cells and rows based on new specs - fixes any scroll styling issues --- components/table/index.css | 86 +++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 34 deletions(-) diff --git a/components/table/index.css b/components/table/index.css index de6567b2029..efc46641d20 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -222,11 +222,15 @@ } } +.spectrum-Table-scroller { + --spectrum-table-header-background-color: var(--mod-table-header-background-color-scrollable, var(--spectrum-background-layer-1-color, var(--spectrum-gray-75))); +} + .spectrum-Table-row--summary { --spectrum-table-cell-background-color: var(--highcontrast-table-row-background-color, var(--mod-table-summary-row-background-color, var(--spectrum-table-summary-row-background-color))); } -/********* REGULAR / DEFAULT *********/ +/********* REGULAR / DEFAULT TABLE *********/ .spectrum-Table:not(.spectrum-Table-scroller), .spectrum-Table-main { border-collapse: separate; @@ -288,7 +292,7 @@ display: inline-block; } -/********* ICONS- SORT, *********/ +/********* ICONS- SORT, DISCLOSURE/MENU *********/ .spectrum-Table-sortIcon { vertical-align: baseline; margin-inline-start: var(--mod-table-sort-icon-inline-start-spacing, 0); @@ -303,6 +307,7 @@ margin-inline-start: var(--mod-table-icon-to-text, var(--spectrum-table-icon-to-text)); } +/********* TEXT ALIGNMENT *********/ .spectrum-Table-cell--alignStart { text-align: start; } @@ -311,10 +316,12 @@ text-align: end; } +/********* TABLE BODY *********/ .spectrum-Table-body { position: relative; border: none; - border-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); + border-end-start-radius: calc(var(--mod-table-border-radius, var(--spectrum-table-border-radius)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); + border-end-end-radius: calc(var(--mod-table-border-radius, var(--spectrum-table-border-radius)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); display: table-row-group; &.is-drop-target { @@ -324,15 +331,17 @@ outline-width: var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness)); outline-style: solid; outline-color: var(--highcontrast-table-focus-indicator-color, var(--mod-table-drop-zone-outline-color, var(--spectrum-table-drop-zone-outline-color))); + outline-offset: calc(-1 * var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness))); } } -/* Outside border, row divider lines, and rounded corners: +/********* CELLS *********/ +.spectrum-Table-cell { + /* Outside border, row divider lines, and rounded corners: The tbody tag doesn't allow setting a border or border-radius. To make them work, border styles are instead applied to the individual cells around the outside, while using border-collapse: separate. */ -/* Row divider lines */ -.spectrum-Table-cell { + /* Row divider lines */ border-block-start: var(--mod-table-border-width, var(--spectrum-table-border-width)) solid var(--highcontrast-table-divider-color, var(--mod-table-divider-color, var(--spectrum-table-divider-color))); /* Cells within table body */ @@ -352,7 +361,28 @@ padding-inline: calc(var(--mod-table-edge-to-content, var(--spectrum-table-edge-to-content)) - var(--mod-table-outer-border-inline-width, var(--spectrum-table-outer-border-inline-width))); } -/* Outside border */ +/* Focus Indicator (Ring) */ +.spectrum-Table-cell, +.spectrum-Table-headCell { + position: relative; + display: table-cell; + + &:focus-visible, + &.is-focused { + outline-width: var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness)); + outline-style: solid; + outline-color: var(--highcontrast-table-cell-focus-indicator-color, var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color)))); + outline-offset: calc((-1 * var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness))) - var(--highcontrast-table-cell-focus-extra-offset, 0px)); + } +} + +/* Cells that display a column divider (vertical border). */ +.spectrum-Table-cell--divider { + border-inline-end: var(--mod-table-border-width, var(--spectrum-table-border-width)) solid var(--highcontrast-table-divider-color, var(--mod-table-divider-color, var(--spectrum-table-divider-color))); +} + +/********* ROWS *********/ +/* Outside border (the table's border) */ .spectrum-Table-body .spectrum-Table-row { &:first-child .spectrum-Table-cell { border-block-start: var(--mod-table-border-width, var(--spectrum-table-border-width)) solid var(--highcontrast-table-border-color, var(--mod-table-border-color, var(--spectrum-table-border-color))); @@ -379,18 +409,22 @@ cursor: var(--mod-table-cursor-row-default, pointer); border-block-start: none; - &:first-child .spectrum-Table-cell:first-child { - border-start-start-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); + /* most cells within the table body have no border-radius */ + .spectrum-Table-cell { + border-radius: 0; } + /* first cell of first row in table body */ &:first-child .spectrum-Table-cell:last-child { - border-start-end-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); + border-radius: 0; } + /* first cell of last row in table body */ &:last-child .spectrum-Table-cell:first-child { border-end-start-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); } + /* last cell of last row in table body */ &:last-child .spectrum-Table-cell:last-child { border-end-end-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); } @@ -413,6 +447,8 @@ &.is-drop-target { /* Make sure negative offset outline is not covered by borders. */ --mod-table-border-color: var(--highcontrast-table-focus-indicator-color, transparent); + border-start-start-radius: 0; + border-start-end-radius: 0; outline-width: var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness)); outline-style: solid; @@ -425,26 +461,6 @@ } } -/* Focus Indicator (Ring) */ -.spectrum-Table-cell, -.spectrum-Table-headCell { - position: relative; - display: table-cell; - - &:focus-visible, - &.is-focused { - outline-width: var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness)); - outline-style: solid; - outline-color: var(--highcontrast-table-cell-focus-indicator-color, var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color)))); - outline-offset: calc((-1 * var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness))) - var(--highcontrast-table-cell-focus-extra-offset, 0px)); - } -} - -/* Cells that display a column divider (vertical border). */ -.spectrum-Table-cell--divider { - border-inline-end: var(--mod-table-border-width, var(--spectrum-table-border-width)) solid var(--highcontrast-table-divider-color, var(--mod-table-divider-color, var(--spectrum-table-divider-color))); -} - .spectrum-Table-row--summary { --spectrum-table-cell-background-color: var(--highcontrast-table-row-background-color, var(--mod-table-summary-row-background-color, var(--spectrum-table-summary-row-background-color))); @@ -488,6 +504,7 @@ } } +/********* CHECKBOX CELLS *********/ .spectrum-Table-checkboxCell { /* Prevent width expansion with 100% width table. */ inline-size: var(--spectrum-checkbox-control-size-small); @@ -530,8 +547,6 @@ /********* SCROLLABLE *********/ /* Wrapper that allows a scrollable body and sticky column header. */ .spectrum-Table-scroller { - --spectrum-table-header-background-color: var(--mod-table-header-background-color-scrollable, var(--spectrum-background-layer-1-color, var(--spectrum-gray-75))); - box-sizing: border-box; display: inline-block; position: relative; @@ -555,11 +570,14 @@ z-index: 2; } - .spectrum-Table-headCell { + /* Remove borders that have been replaced by borders on scroller wrapper or pseudo elements. */ + .spectrum-Table-headRow:first-child .spectrum-Table-headCell { border-block-end: var(--mod-table-border-width, var(--spectrum-table-border-width)) solid var(--highcontrast-table-border-color, var(--mod-table-border-color, var(--spectrum-table-border-color))); + border-block-start: 0; + border-inline: 0; + border-radius: 0; } - /* Remove borders that have been replaced by borders on wrapper or pseudo elements. */ .spectrum-Table-body .spectrum-Table-row { &:first-child .spectrum-Table-cell { border-block-start: none; From 806d915e734fe883624f70c8e50df7f98cf08a80 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 21 May 2025 08:49:58 -0400 Subject: [PATCH 08/52] docs(table): updates to stories - uses sentence-case for story names - updates to some display names and cell organization --- components/table/stories/table.stories.js | 39 ++++++++++++----------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index 950cb73b76c..8ac9867bfce 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -174,17 +174,18 @@ EmptyState.args = { EmptyState.parameters = { chromatic: { disableSnapshot: true }, }; +EmptyState.storyName = "Empty state"; -export const Loading = Template.bind({}); -Loading.args = { +export const LoadingState = Template.bind({}); +LoadingState.args = { ...Default.args, isLoading: true, }; -Loading.tags = ["!dev"]; -Loading.parameters = { +LoadingState.tags = ["!dev"]; +LoadingState.parameters = { chromatic: { disableSnapshot: true }, }; - +LoadingState.storyName = "Loading state"; /** * The compact variant decreases the spacing used within the table rows, except for the header row. */ @@ -396,6 +397,10 @@ WithColumnDividers.parameters = { export const SummaryAndSelected = Template.bind({}); SummaryAndSelected.args = { rowItems: [ + { + cellContent: "Summary row", + isSummaryRow: true, + }, { cellContent: "Table row alpha", }, @@ -409,13 +414,9 @@ SummaryAndSelected.args = { { cellContent: "Table row delta", }, - { - cellContent: "Summary row", - isSummaryRow: true, - }, ], }; -SummaryAndSelected.storyName = "Summary and selected"; +SummaryAndSelected.storyName = "Summary and selected rows"; SummaryAndSelected.parameters = { chromatic: { disableSnapshot: true }, }; @@ -451,7 +452,7 @@ SectionHeader.args = { }, ], }; -SectionHeader.storyName = "Section header"; +SectionHeader.storyName = "Section headers"; SectionHeader.parameters = { chromatic: { disableSnapshot: true }, }; @@ -461,7 +462,7 @@ SectionHeaderQuiet.args = { ...SectionHeader.args, isQuiet: true, }; -SectionHeaderQuiet.storyName = "Section header: quiet"; +SectionHeaderQuiet.storyName = "Section headers: quiet styling"; SectionHeaderQuiet.parameters = { chromatic: { disableSnapshot: true }, }; @@ -486,7 +487,7 @@ Scrollable.args = { cellContent: "Table row bravo", }, { - cellContent: "Table row charlie", + cellContent: "Selected row charlie", isSelected: true, }, { @@ -513,7 +514,7 @@ Scrollable.parameters = { * A table can also be made up of `div` tags if needed, instead of a ``. This example uses both the div markup, and the scrollable wrapper. */ export const DivsScrollable = Template.bind({}); -DivsScrollable.storyName = "Scrollable with divs"; +DivsScrollable.storyName = "Scrollable table with divs"; DivsScrollable.args = { ...Scrollable.args, useDivs: true, @@ -588,6 +589,7 @@ Collapsible.args = { Collapsible.parameters = { chromatic: { disableSnapshot: true }, }; +Collapsible.storyName = "Collapsible rows"; export const CollapsibleMultiSelect = Template.bind({}); CollapsibleMultiSelect.storyName = "Selection mode: multiple, collapsible rows"; @@ -684,6 +686,7 @@ Visuals.args = { Visuals.parameters = { chromatic: { disableSnapshot: true }, }; +Visuals.storyName = "With visuals"; /** * The table can also combine visuals with collapsible rows. @@ -750,7 +753,7 @@ VisualsCollapsible.args = { }, ], }; -VisualsCollapsible.storyName = "Visuals: collapsible"; +VisualsCollapsible.storyName = "With visuals: collapsible rows"; VisualsCollapsible.parameters = { chromatic: { disableSnapshot: true }, }; @@ -764,7 +767,7 @@ BodyDropZone.args = { ...Default.args, isDropTarget: true, }; -BodyDropZone.tags = ["!autodocs","!dev"]; +BodyDropZone.tags = ["!autodocs", "!dev"]; BodyDropZone.storyName = "Dropzone: body"; BodyDropZone.parameters = { chromatic: { disableSnapshot: true }, @@ -799,7 +802,7 @@ RowDropZone.args = { }, ], }; -RowDropZone.tags = ["!autodocs","!dev"]; +RowDropZone.tags = ["!autodocs", "!dev"]; RowDropZone.storyName = "Dropzone: row"; RowDropZone.parameters = { chromatic: { disableSnapshot: true }, @@ -810,7 +813,7 @@ RowDropZoneQuiet.args = { ...RowDropZone.args, isQuiet: true, }; -RowDropZoneQuiet.tags = ["!autodocs","!dev"]; +RowDropZoneQuiet.tags = ["!autodocs", "!dev"]; RowDropZoneQuiet.storyName = "Dropzone: row, quiet"; RowDropZoneQuiet.parameters = { chromatic: { disableSnapshot: true }, From 67606b7f89b44d1548215eb187bc6726e0b27e82 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 21 May 2025 11:21:03 -0400 Subject: [PATCH 09/52] feat(table): updates visual styles - updates the thumbnail styles to "visual" to reflect the avatar, icon, and thumbnail support - minor changes to code comments for clarity - fixes vertical alignment for sort and disclosure/menu icons --- components/table/index.css | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/components/table/index.css b/components/table/index.css index efc46641d20..8daf6b67737 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -294,9 +294,9 @@ /********* ICONS- SORT, DISCLOSURE/MENU *********/ .spectrum-Table-sortIcon { - vertical-align: baseline; + vertical-align: var(--mod-table-header-vertical-align, var(--spectrum-table-header-vertical-align)); margin-inline-start: var(--mod-table-sort-icon-inline-start-spacing, 0); - margin-inline-end: var(--mod-table-sort-icon-inline-end-spacing, var(--mod-table-icon-to-text, var(--spectrum-table-icon-to-text))); + margin-inline-end: var(--mod-table-sort-icon-inline-end-spacing, var(--mod-table-visual-to-text, var(--spectrum-table-visual-to-text))); transition: transform var(--highcontrast-table-transition-duration, var(--mod-table-transition-duration, var(--spectrum-table-transition-duration))) ease-in-out; color: var(--spectrum-table-icon-color); display: inline-block; @@ -304,7 +304,7 @@ .spectrum-Table-menuIcon { vertical-align: middle; - margin-inline-start: var(--mod-table-icon-to-text, var(--spectrum-table-icon-to-text)); + margin-inline-start: var(--mod-table-visual-to-text, var(--spectrum-table-visual-to-text)); } /********* TEXT ALIGNMENT *********/ @@ -358,7 +358,7 @@ padding-block-start: calc(var(--mod-table-row-top-to-text, var(--spectrum-table-row-top-to-text)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); padding-block-end: var(--mod-table-row-bottom-to-text, var(--spectrum-table-row-bottom-to-text)); - padding-inline: calc(var(--mod-table-edge-to-content, var(--spectrum-table-edge-to-content)) - var(--mod-table-outer-border-inline-width, var(--spectrum-table-outer-border-inline-width))); + padding-inline: calc(var(--mod-table-cell-inline-spacing, var(--spectrum-table-cell-inline-spacing)) - var(--mod-table-outer-border-inline-width, var(--spectrum-table-outer-border-inline-width))); } /* Focus Indicator (Ring) */ @@ -719,8 +719,8 @@ } } -/********* THUMBNAILS *********/ -.spectrum-Table-row--thumbnail { +/********* VISUALS- THUMBNAILS, AVATARS, ICONS *********/ +.spectrum-Table-row--visual { --spectrum-table-thumbnail-cell-block-spacing: var(--mod-table-thumbnail-block-spacing, var(--spectrum-table-thumbnail-block-spacing)); --spectrum-table-thumbnail-inner-content-block-spacing: max(0px, calc((var(--mod-table-thumbnail-size, var(--spectrum-table-thumbnail-size)) - (var(--mod-table-row-line-height, var(--spectrum-table-row-line-height)) * var(--mod-table-header-font-size, var(--spectrum-table-row-font-size)))) / 2)); @@ -730,7 +730,7 @@ } /* Cell that does have a thumbnail has its padding moved within the "Inner" and "Content" alignment wrappers. */ - .spectrum-Table-cell--thumbnail { + .spectrum-Table-cell--visual { padding-block: 0; } @@ -741,29 +741,32 @@ } } -.spectrum-Table-thumbnailInner { +.spectrum-Table-visualInner { display: flex; flex-direction: row; flex-wrap: nowrap; justify-content: flex-start; align-items: flex-start; + gap: var(--spectrum-table-visual-to-text); padding-block: var(--spectrum-table-thumbnail-cell-block-spacing); - .spectrum-Thumbnail { + .spectrum-Thumbnail, + .spectrum-Icon, + .spectrum-Avatar { flex-grow: 0; flex-shrink: 0; margin-inline-end: var(--mod-table-thumbnail-to-text, var(--spectrum-table-thumbnail-to-text)); line-height: var(--mod-table-row-line-height, var(--spectrum-table-row-line-height)); } - .spectrum-Table-thumbnailContent { - /* Vertically centers text with the middle of the thumbnail. */ - padding-block: var(--spectrum-table-thumbnail-inner-content-block-spacing); + .spectrum-Table-visualContent { + /* Vertically centers text with the middle of the cell with the visual. */ + padding-block: var(--spectrum-table-visual-inner-content-block-spacing); } } /********* TESTS DURING DEVELOPMENT *********/ -.spectrum-Table-cell { +/* .spectrum-Table-cell { border-radius: calc(var(--spectrum-table-border-radius) - var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness))); } From c5963761ff01434ad6254e97f10cb0809b5c77f0 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 21 May 2025 12:28:28 -0400 Subject: [PATCH 10/52] chore(table): whitespace changes and visual/collapsible handling - fixes the indentation for the isCollapsible block - removes columnIndex limit for visual elements - adds tabIndex to cells and rows to support keyboard navigation and design specs - adds -headRow class name for header rows - adds isFocused arg for rows --- components/table/stories/template.js | 94 +++++++++++++++------------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/components/table/stories/template.js b/components/table/stories/template.js index ee662aa2295..c0dfcfbb463 100644 --- a/components/table/stories/template.js +++ b/components/table/stories/template.js @@ -18,6 +18,7 @@ export const TableRowItem = ({ cellContent = "Row Item Text", showCheckbox = false, isSelected = false, + isFocused = false, isSummaryRow = false, isSectionHeader = false, isEmphasized = true, @@ -48,7 +49,7 @@ export const TableRowItem = ({ ? cellContent[columnIndex] : cellContent; - if (useVisuals && columnIndex < 2) { + if (useVisuals) { return html`
${visualElement === "thumbnail" ? @@ -80,9 +81,7 @@ export const TableRowItem = ({ // For each column, apply the text alignment specified in textAlignment const getTextAlignment = (columnIndex) => { - if (!textAlignment) return "start"; - - return textAlignment[columnIndex] || "start"; + return textAlignment?.[columnIndex] || "start"; }; return html` @@ -94,6 +93,7 @@ export const TableRowItem = ({ [`${rootClass}-row--collapsible`]: isCollapsible, [`${rootClass}-cell--divider`]: useColumnDividers, ["is-selected"]: isSelected, + ["is-focused"]: isFocused, ["is-expanded"]: isExpanded, ["is-last-tier"]: isLastTier, ["is-drop-target"]: isDropTarget, @@ -103,6 +103,7 @@ export const TableRowItem = ({ aria-selected=${ifDefined(showCheckbox ? "true" : undefined)} data-tier=${ifDefined(tier)} ?hidden=${isHidden} + tabindex="0" > ${when(showCheckbox && !isSectionHeader, () => html` <${cellTag} @@ -112,6 +113,7 @@ export const TableRowItem = ({ [`${rootClass}-checkboxCell`]: true, [`${rootClass}-cell--alignEnd`]: getTextAlignment(0) === "end", })} + tabindex="0" > ${when(!isSummaryRow, () => Checkbox({ @@ -125,43 +127,47 @@ export const TableRowItem = ({ )} ${isCollapsible - ? html` - <${cellTag} - role=${ifDefined(showCheckbox ? "gridcell" : useDivs ? "cell" : undefined)} - class=${classMap({ - [`${rootClass}-cell`]: true, - [`${rootClass}-cell--collapsible`]: true, - [`${rootClass}-cell--visual`]: useVisuals, - [`${rootClass}-cell--divider`]: useColumnDividers, - [`${rootClass}-cell--alignEnd`]: getTextAlignment(0) === "end", - })} - > -
- ${when(!isLastTier, () => - Button({ - size: "m", - iconName: "ChevronRight100", - iconSet: "ui", - hideLabel: true, - customClasses: [`${rootClass}-disclosureIcon`], - ariaExpanded: isExpanded, - ariaControls, - }, context) - )} - ${useVisuals ? getCellContent(0) : html`
${getCellContent(0)}
`} -
- ` - : html` - <${cellTag} - role=${ifDefined(showCheckbox ? "gridcell" : useDivs ? "cell" : undefined)} - class=${classMap({ - [`${rootClass}-cell`]: true, - [`${rootClass}-cell--visual`]: useVisuals, - [`${rootClass}-cell--divider`]: useColumnDividers, - [`${rootClass}-cell--alignEnd`]: getTextAlignment(0) === "end", - })} - colspan=${ifDefined(isSectionHeader && showCheckbox ? "4" : isSectionHeader ? "3" : undefined)} - >${getCellContent(0)}` + ? html` + <${cellTag} + role=${ifDefined(showCheckbox ? "gridcell" : useDivs ? "cell" : undefined)} + class=${classMap({ + [`${rootClass}-cell`]: true, + [`${rootClass}-cell--collapsible`]: true, + [`${rootClass}-cell--visual`]: useVisuals, + [`${rootClass}-cell--divider`]: useColumnDividers, + [`${rootClass}-cell--alignEnd`]: getTextAlignment(0) === "end", + })} + tabindex="0" + > +
+ ${when(!isLastTier, () => + Button({ + size: "m", + iconName: "ChevronRight100", + iconSet: "ui", + hideLabel: true, + customClasses: [`${rootClass}-disclosureIcon`], + ariaExpanded: isExpanded, + ariaControls, + }, context) + )} + ${useVisuals ? getCellContent(0) : html`
${getCellContent(0)}
`} +
+ ` + : html` + <${cellTag} + role=${ifDefined(showCheckbox ? "gridcell" : useDivs ? "cell" : undefined)} + class=${classMap({ + [`${rootClass}-cell`]: true, + [`${rootClass}-cell--visual`]: useVisuals, + [`${rootClass}-cell--divider`]: useColumnDividers, + [`${rootClass}-cell--alignEnd`]: getTextAlignment(0) === "end", + })} + colspan=${ifDefined(isSectionHeader && showCheckbox ? "4" : isSectionHeader ? "3" : undefined)} + tabindex="0" + > + ${getCellContent(0)} + ` } ${when(!isSectionHeader, () => html` @@ -173,6 +179,7 @@ export const TableRowItem = ({ [`${rootClass}-cell--divider`]: useColumnDividers, [`${rootClass}-cell--alignEnd`]: getTextAlignment(1) === "end", })} + tabindex="0" >${getCellContent(1)} <${cellTag} @@ -182,6 +189,7 @@ export const TableRowItem = ({ [`${rootClass}-cell--divider`]: useColumnDividers, [`${rootClass}-cell--alignEnd`]: getTextAlignment(2) === "end", })} + tabindex="0" >${getCellContent(2)}` )} @@ -197,7 +205,7 @@ export const Template = ({ useDivs = false, selectionMode = "none", useScroller = false, - visualElement = {}, + visualElement, isDropTarget = false, hasColumnDividers = false, rowItems = [], @@ -262,7 +270,6 @@ export const Template = ({ id=${ifDefined(id)} role=${ifDefined(useCheckboxCell ? "grid" : useDivs ? "table" : undefined)} aria-multiselectable=${ifDefined(useCheckboxCell ? "true" : undefined)} - style="max-width: 800px;" > <${theadTag} class="${rootClass}-head" @@ -270,6 +277,7 @@ export const Template = ({ > <${rowTag} role=${ifDefined(useDivs ? "row" : undefined)} + class="${rootClass}-headRow" > ${when(useCheckboxCell, () => html` <${thTag} From 5ec91c65af20964a8562ef677f482302be1368d1 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 21 May 2025 13:43:11 -0400 Subject: [PATCH 11/52] feat(table): styles for section headers and summary rows - reorganizes custom properties for section headers and summary rows into the rest of the custom properties - corrects some styles for section headers - adds some mods for summary rows and section headers - corrects some styling for checkboxes --- components/table/index.css | 91 +++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 36 deletions(-) diff --git a/components/table/index.css b/components/table/index.css index 8daf6b67737..f1641ced3b3 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -36,9 +36,6 @@ /* Row Selection */ --spectrum-table-row-active-color: rgb(var(--spectrum-gray-900-rgb), var(--spectrum-table-row-down-opacity)); /* active/down state background color */ - --spectrum-table-header-row-checkbox-block-spacing: var(--spectrum-table-header-row-checkbox-to-top-medium); - --spectrum-table-row-checkbox-block-spacing: var(--spectrum-table-row-checkbox-to-top-medium-regular); - /* Border */ --spectrum-table-border-color: var(--spectrum-gray-300); --spectrum-table-border-radius: var(--spectrum-corner-radius-medium-size-extra-small); @@ -48,9 +45,6 @@ /* Divider */ --spectrum-table-divider-color: var(--spectrum-gray-300); - /* Cells */ - --spectrum-table-cell-inline-space: var(--spectrum-table-edge-to-content); - /* Focus indicators */ --spectrum-table-focus-indicator-thickness: var(--spectrum-focus-indicator-thickness); --spectrum-table-focus-indicator-color: var(--spectrum-focus-indicator-color); @@ -89,11 +83,13 @@ --spectrum-table-min-header-row-height: var(--spectrum-component-height-100); --spectrum-table-header-row-top-to-text: var(--spectrum-table-column-header-row-top-to-text-medium); --spectrum-table-header-row-bottom-to-text: var(--spectrum-table-column-header-row-bottom-to-text-medium); - --spectrum-table-icon-to-text: var(--spectrum-text-to-visual-300); --spectrum-table-min-row-height: var(--spectrum-table-row-height-medium); - --spectrum-table-row-top-to-text: var(--spectrum-table-row-top-to-text-medium-regular); - --spectrum-table-row-bottom-to-text: var(--spectrum-table-row-bottom-to-text-medium-regular); + --spectrum-table-row-top-to-text: var(--spectrum-table-row-top-to-text-medium); + --spectrum-table-row-bottom-to-text: var(--spectrum-table-row-bottom-to-text-medium); + + --spectrum-table-header-row-checkbox-block-spacing: var(--spectrum-table-header-row-checkbox-to-top-medium); + --spectrum-table-row-checkbox-block-spacing: var(--spectrum-table-row-checkbox-to-top-medium); --spectrum-table-section-header-min-height: var(--spectrum-table-section-header-row-height-medium); --spectrum-table-section-header-block-start-spacing: var(--spectrum-component-top-to-text-100); @@ -103,6 +99,7 @@ --spectrum-table-visual-to-text: var(--spectrum-text-to-visual-300); --spectrum-table-thumbnail-size: var(--spectrum-thumbnail-size-300); + --spectrum-table-cell-inline-spacing: var(--spectrum-table-edge-to-content); --spectrum-table-checkbox-to-cell-content: var(--spectrum-table-checkbox-to-text); --spectrum-table-collapsible-tier-indent: var(--spectrum-spacing-300); @@ -121,8 +118,8 @@ .spectrum-Table--compact { /* Size and Spacing */ --spectrum-table-min-row-height: var(--mod-table-min-row-height-compact, var(--spectrum-table-row-height-medium-compact)); - --spectrum-table-header-row-top-to-text: var(--mod-table-row-top-to-text-compact, var(--spectrum-table-row-top-to-text-medium-compact)); - --spectrum-table-header-row-bottom-to-text: var(--mod-table-row-bottom-to-text-compact, var(--spectrum-table-row-bottom-to-text-medium-compact)); + --spectrum-table-row-top-to-text: var(--spectrum-table-row-top-to-text-medium-compact); + --spectrum-table-row-bottom-to-text: var(--spectrum-table-row-bottom-to-text-medium-compact); /* Row Selection */ --spectrum-table-row-checkbox-block-spacing: var(--mod-table-row-checkbox-block-spacing-compact, var(--spectrum-table-row-checkbox-to-top-medium-compact)); @@ -135,8 +132,8 @@ .spectrum-Table--spacious { /* Size and Spacing */ --spectrum-table-min-row-height: var(--mod-table-min-row-height-spacious, var(--spectrum-table-row-height-medium-spacious)); - --spectrum-table-header-row-top-to-text: var(--mod-table-row-top-to-text-spacious, var(--spectrum-table-row-top-to-text-medium-spacious)); - --spectrum-table-header-row-bottom-to-text: var(--mod-table-row-bottom-to-text-spacious, var(--spectrum-table-row-bottom-to-text-medium-spacious)); + --spectrum-table-row-top-to-text: var(--spectrum-table-row-top-to-text-medium-spacious); + --spectrum-table-row-bottom-to-text: var(--spectrum-table-row-bottom-to-text-medium-spacious); /* Row Selection */ --spectrum-table-row-checkbox-block-spacing: var(--mod-table-row-checkbox-block-spacing-spacious, var(--spectrum-table-row-checkbox-to-top-medium-spacious)); @@ -230,6 +227,15 @@ --spectrum-table-cell-background-color: var(--highcontrast-table-row-background-color, var(--mod-table-summary-row-background-color, var(--spectrum-table-summary-row-background-color))); } +.spectrum-Table-row--sectionHeader { + --spectrum-table-cell-background-color: var(--highcontrast-table-section-header-background-color, var(--mod-table-section-header-background-color, var(--spectrum-table-section-header-background-color))); + + &:hover { + --highcontrast-table-row-text-color: var(--highcontrast-table-section-header-text-color); + --spectrum-table-cell-background-color: var(--highcontrast-table-section-header-background-color, var(--mod-table-section-header-background-color, var(--spectrum-table-section-header-background-color))); + } +} + /********* REGULAR / DEFAULT TABLE *********/ .spectrum-Table:not(.spectrum-Table-scroller), .spectrum-Table-main { @@ -255,7 +261,7 @@ block-size: var(--mod-table-min-header-height, var(--spectrum-table-min-header-row-height)); padding-block: var(--mod-table-header-row-top-to-text, var(--spectrum-table-header-row-top-to-text)) var(--mod-table-header-row-bottom-to-text, var(--spectrum-table-header-row-bottom-to-text)); - padding-inline: var(--mod-table-cell-inline-space, var(--spectrum-table-cell-inline-space)); + padding-inline: var(--mod-table-cell-inline-spacing, var(--spectrum-table-cell-inline-spacing)); color: var(--mod-table-header-text-color, var(--spectrum-table-header-text-color)); background-color: var(--mod-table-header-background-color, var(--spectrum-table-header-background-color)); @@ -279,6 +285,29 @@ border-start-end-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); border-inline-end: var(--mod-table-border-width, var(--spectrum-table-border-width)) solid var(--highcontrast-table-border-color, var(--mod-table-border-color, var(--spectrum-table-border-color))); } + + /* Checkbox cell in header row */ + .spectrum-Table-checkboxCell { + padding-inline-start: calc(var(--mod-table-cell-inline-spacing, var(--spectrum-table-cell-inline-spacing)) - var(--mod-table-outer-border-inline-width, var(--spectrum-table-outer-border-inline-width))); + + /* the calc subtraction is because the --spectrum-table-checkbox-to-text spacing value + includes the inline start padding in the adjacent cell. */ + padding-inline-end: calc(var(--mod-table-checkbox-to-cell-content, var(--spectrum-table-checkbox-to-cell-content)) - var(--mod-table-cell-inline-spacing, var(--spectrum-table-cell-inline-spacing))); + padding-block: var(--mod-table-header-row-checkbox-block-spacing, var(--spectrum-table-header-row-checkbox-block-spacing)); + + .spectrum-Table-checkbox { + --mod-checkbox-margin-block: 0px; + + min-block-size: initial; + } + } +} + +/* The quiet table's header row cells need a border-block-end to match the body cells. */ +.spectrum-Table--quiet { + .spectrum-Table-headCell { + border-block-end: var(--mod-table-border-width, var(--spectrum-table-border-width)) solid var(--highcontrast-table-divider-color, var(--mod-table-divider-color, var(--spectrum-table-divider-color))); + } } .spectrum-Table-sortIcon { @@ -462,12 +491,14 @@ } .spectrum-Table-row--summary { - --spectrum-table-cell-background-color: var(--highcontrast-table-row-background-color, var(--mod-table-summary-row-background-color, var(--spectrum-table-summary-row-background-color))); - .spectrum-Table-cell { font-weight: var(--mod-table-summary-row-font-weight, var(--spectrum-table-summary-row-font-weight)); - /* Make unique summary row mods available to these default row styles: */ + /* Summary rows are typically the same dimensions as other non-header rows, but we make + mods available to override the default row styles */ + block-size: var(--mod-table-summary-row-min-height, var(--spectrum-table-min-row-height)); + padding-block-start: calc(var(--mod-table-summary-row-top-to-text, var(--spectrum-table-row-top-to-text)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); + padding-block-end: var(--mod-table-summary-row-bottom-to-text, var(--spectrum-table-row-bottom-to-text)); font-size: var(--mod-table-summary-row-font-size, var(--spectrum-table-row-font-size)); font-family: var(--mod-table-summary-row-font-family, var(--spectrum-table-row-font-family)); font-style: var(--mod-table-summary-row-font-style, var(--spectrum-table-row-font-style)); @@ -477,7 +508,6 @@ } .spectrum-Table-row--sectionHeader { - --spectrum-table-cell-background-color: var(--highcontrast-table-section-header-background-color, var(--mod-table-section-header-background-color, var(--spectrum-table-section-header-background-color))); cursor: var(--mod-table-cursor-section-header, initial); .spectrum-Table-cell { @@ -491,19 +521,16 @@ padding-block-end: calc(var(--mod-table-section-header-block-end-spacing, var(--spectrum-table-section-header-block-end-spacing)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); /* Make unique section header mods available to these default row styles: */ + padding-inline-start: calc(var(--mod-table-section-header-inline-start-spacing, var(--spectrum-table-cell-inline-spacing)) - var(--mod-table-outer-border-inline-width, var(--spectrum-table-outer-border-inline-width))); font-size: var(--mod-table-section-header-font-size, var(--spectrum-table-row-font-size)); font-family: var(--mod-table-section-header-font-family, var(--spectrum-table-row-font-family)); font-style: var(--mod-table-section-header-font-style, var(--spectrum-table-row-font-style)); line-height: var(--mod-table-section-header-line-height, var(--spectrum-table-row-line-height)); color: var(--highcontrast-table-section-header-text-color, var(--mod-table-section-header-text-color, var(--spectrum-table-row-text-color))); } - - &:hover { - --highcontrast-table-row-text-color: var(--highcontrast-table-section-header-text-color); - --spectrum-table-cell-background-color: var(--highcontrast-table-section-header-background-color, var(--mod-table-section-header-background-color, var(--spectrum-table-section-header-background-color))); - } } +/* Checkbox cell styles are further down the cascade to override some of the checkbox styles for collapsible tables. */ /********* CHECKBOX CELLS *********/ .spectrum-Table-checkboxCell { /* Prevent width expansion with 100% width table. */ @@ -511,22 +538,16 @@ /* The calc subtraction is because the --spectrum-table-checkbox-to-text spacing value includes the inline start padding in the adjacent cell. */ - padding-inline-end: calc(var(--mod-table-checkbox-to-text, var(--spectrum-table-checkbox-to-text)) - var(--mod-table-edge-to-content, var(--spectrum-table-edge-to-content))); - - /* Block spacing must be moved to calculated margin on the checkbox element. */ - padding-block: 0; + padding-inline-end: calc(var(--mod-table-checkbox-to-cell-content, var(--spectrum-table-checkbox-to-cell-content)) - var(--mod-table-cell-inline-spacing, var(--spectrum-table-cell-inline-spacing))); + padding-block-start: calc(var(--mod-table-row-checkbox-block-spacing, var(--spectrum-table-row-checkbox-block-spacing)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); + padding-block-end: var(--mod-table-row-checkbox-block-spacing, var(--spectrum-table-row-checkbox-block-spacing)); .spectrum-Table-checkbox { - --mod-checkbox-spacing: 0px; + --mod-checkbox-margin-block: 0px; min-block-size: initial; } - &.spectrum-Table-cell .spectrum-Table-checkbox { - margin-block-start: calc(var(--mod-table-row-checkbox-block-spacing, var(--spectrum-table-row-checkbox-block-spacing)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); - margin-block-end: var(--mod-table-row-checkbox-block-spacing, var(--spectrum-table-row-checkbox-block-spacing)); - } - &.spectrum-Table-headCell .spectrum-Table-checkbox { margin-block-start: calc(var(--mod-table-header-checkbox-block-spacing, var(--spectrum-table-header-checkbox-block-spacing)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); margin-block-end: var(--mod-table-header-checkbox-block-spacing, var(--spectrum-table-header-checkbox-block-spacing)); @@ -654,7 +675,6 @@ .spectrum-Table-cell--collapsible { padding-inline-start: calc(var(--spectrum-table-row-tier, 0px) * var(--spectrum-table-collapsible-tier-indent)); - padding-block: 0; } .spectrum-Table-collapseInner { @@ -665,7 +685,6 @@ align-items: flex-start; .spectrum-Table-collapseContent { - padding-block-start: calc(var(--mod-table-row-top-to-text, var(--spectrum-table-row-top-to-text)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); padding-block-end: var(--mod-table-row-bottom-to-text, var(--spectrum-table-row-bottom-to-text)); } } @@ -789,7 +808,7 @@ --spectrum-table-focus-indicator-shadow: inset; box-shadow: var(--spectrum-table-focus-indicator-shadow) var(--spectrum-side-focus-indicator) 0 0 0 var(--spectrum-table-focus-indicator-color); outline: none; -} +} */ /********* HIGH CONTRAST *********/ @media (forced-colors: active) { From d6feccfc45c6c7ad5cd7315830c73f8322c9d19d Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 21 May 2025 14:13:27 -0400 Subject: [PATCH 12/52] feat(table): vertically center all text in cells --- components/table/index.css | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/components/table/index.css b/components/table/index.css index f1641ced3b3..16518d28964 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -62,8 +62,7 @@ --spectrum-table-icon-color: var(--highcontrast-table-icon-color, var(--mod-table-icon-color-default, var(--spectrum-table-icon-color-default))); /* Alignment */ - --spectrum-table-default-vertical-align: top; - --spectrum-table-header-vertical-align: middle; + --spectrum-table-default-vertical-align: middle; /* Font */ --spectrum-table-header-font-weight: var(--spectrum-bold-font-weight); @@ -249,7 +248,7 @@ .spectrum-Table-headCell { box-sizing: border-box; text-align: start; - vertical-align: var(--mod-table-header-vertical-align, var(--spectrum-table-header-vertical-align)); + vertical-align: var(--mod-table-header-vertical-align, var(--spectrum-table-default-vertical-align)); font-family: var(--mod-table-header-font-family, var(--spectrum-table-row-font-family)); font-size: var(--mod-table-header-font-size, var(--spectrum-table-row-font-size)); @@ -323,7 +322,7 @@ /********* ICONS- SORT, DISCLOSURE/MENU *********/ .spectrum-Table-sortIcon { - vertical-align: var(--mod-table-header-vertical-align, var(--spectrum-table-header-vertical-align)); + vertical-align: var(--mod-table-header-vertical-align, var(--spectrum-table-default-vertical-align)); margin-inline-start: var(--mod-table-sort-icon-inline-start-spacing, 0); margin-inline-end: var(--mod-table-sort-icon-inline-end-spacing, var(--mod-table-visual-to-text, var(--spectrum-table-visual-to-text))); transition: transform var(--highcontrast-table-transition-duration, var(--mod-table-transition-duration, var(--spectrum-table-transition-duration))) ease-in-out; @@ -682,11 +681,7 @@ flex-direction: row; flex-wrap: nowrap; justify-content: flex-start; - align-items: flex-start; - - .spectrum-Table-collapseContent { - padding-block-end: var(--mod-table-row-bottom-to-text, var(--spectrum-table-row-bottom-to-text)); - } + align-items: center; } .spectrum-Table-disclosureIcon.spectrum-Table-disclosureIcon { @@ -765,7 +760,7 @@ flex-direction: row; flex-wrap: nowrap; justify-content: flex-start; - align-items: flex-start; + align-items: center; gap: var(--spectrum-table-visual-to-text); padding-block: var(--spectrum-table-thumbnail-cell-block-spacing); From 8264493d19252854b36ff243eb1b508a208786c2 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 21 May 2025 15:32:34 -0400 Subject: [PATCH 13/52] docs(table): adds migrated badge --- components/table/stories/table.stories.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index 8ac9867bfce..235a9e0e8ec 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -118,7 +118,11 @@ export default { }, packageJson, metadata, + status: { + type: "migrated" + }, }, + tags: ["migrated"], }; From 73ce93949453f1efe35287275d2aae8ba22ec4c0 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 21 May 2025 16:44:06 -0400 Subject: [PATCH 14/52] feat(table): styles for focus indicators - adds focus indicators for cells and rows - opts for a pseudo-element for the focus indicator instead of outline - adds cellCustomClasses to support focused cells - adds a TableStates story to show the focus and selected states --- components/table/index.css | 81 ++++++++++++++--------- components/table/stories/table.stories.js | 6 ++ components/table/stories/template.js | 6 ++ 3 files changed, 62 insertions(+), 31 deletions(-) diff --git a/components/table/index.css b/components/table/index.css index 16518d28964..48c2c4fa153 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -48,6 +48,7 @@ /* Focus indicators */ --spectrum-table-focus-indicator-thickness: var(--spectrum-focus-indicator-thickness); --spectrum-table-focus-indicator-color: var(--spectrum-focus-indicator-color); + --spectrum-table-row-focus-indicator-width: var(--spectrum-side-focus-indicator); --spectrum-table-selected-cell-background-color-focus: var(--highcontrast-table-selected-row-background-color-focus, var(--mod-table-selected-row-background-color-non-emphasized-focus, var(--spectrum-table-selected-row-background-color-non-emphasized-focus))); @@ -397,10 +398,20 @@ &:focus-visible, &.is-focused { - outline-width: var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness)); - outline-style: solid; - outline-color: var(--highcontrast-table-cell-focus-indicator-color, var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color)))); - outline-offset: calc((-1 * var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness))) - var(--highcontrast-table-cell-focus-extra-offset, 0px)); + position: relative; + + /* Remove the outline since we'll replace it with an ::after pseudo-element */ + outline: none; + z-index: 1; /* Ensure cell focus indicator appears above row focus indicator */ + + &::after { + content: ""; + position: absolute; + inset: calc(-1 * var(--mod-table-border-width, var(--spectrum-table-border-width))); + border-radius: calc(var(--mod-table-border-radius, var(--spectrum-table-border-radius)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); + border: var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness)) solid var(--highcontrast-table-cell-focus-indicator-color, var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color)))); + pointer-events: none; + } } } @@ -468,8 +479,30 @@ border-end-end-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); } + /* Row focus indicator */ + &.is-focused, + &:focus-visible, &:focus { - outline: 0; + .spectrum-Table-cell:first-child { + position: relative; + overflow: hidden; + outline: none; + + &::before { + content: ""; + position: absolute; + inset-block-start: 0; + inset-block-end: 0; + inset-inline-start: 0; + inline-size: var(--spectrum-side-focus-indicator); + background-color: var(--spectrum-table-focus-indicator-color); + z-index: 1; + } + } + + &:last-child .spectrum-Table-cell:first-child::before { + border-end-start-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); + } } &.is-drop-target { @@ -779,32 +812,6 @@ } } -/********* TESTS DURING DEVELOPMENT *********/ -/* .spectrum-Table-cell { - border-radius: calc(var(--spectrum-table-border-radius) - var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness))); -} - -.spectrum-Table-cell.is-focused { - outline-width: var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness)); - outline-style: solid; - outline-color: var(--highcontrast-table-cell-focus-indicator-color, var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color)))); - outline-offset: calc(var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness)) - 2px); -} - -.spectrum-Table-cell.is-selected { - background-color: var(--spectrum-table-selected-row-background-color); -} - -.spectrum-Table-row.is-selected { - --mod-table-row-background-color: var(--spectrum-table-selected-row-background-color); -} - -.spectrum-Table-row.is-focused .spectrum-Table-cell:first-child { - --spectrum-table-focus-indicator-shadow: inset; - box-shadow: var(--spectrum-table-focus-indicator-shadow) var(--spectrum-side-focus-indicator) 0 0 0 var(--spectrum-table-focus-indicator-color); - outline: none; -} */ - /********* HIGH CONTRAST *********/ @media (forced-colors: active) { .spectrum-Table { @@ -838,6 +845,13 @@ .spectrum-Table-cell { /* Removes unstylable readability backplate. */ forced-color-adjust: none; + + &:focus-visible, + &.is-focused { + &::after { + border-color: Highlight; + } + } } .spectrum-Table-row { @@ -847,6 +861,11 @@ .spectrum-Table-checkbox .spectrum-Checkbox-box::before { outline: var(--highcontrast-table-row-text-color-hover) 1px solid; } + + /* corresponds to the row focus indicator with a ::before pseudo element */ + .spectrum-Table-cell:first-child::before { + background-color: Highlight; + } } } diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index 235a9e0e8ec..01693dfd8b7 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -336,6 +336,12 @@ TableStates.args = { cellContent: "Focused unselected row, no rounded corners", isFocused: true, }, + { + cellContent: ["Table row with a focused cell", "Focused cell", "Unfocused cell"], + cellCustomClasses: { + 1: ["is-focused"] + } + }, { cellContent: "Table row echo", }, diff --git a/components/table/stories/template.js b/components/table/stories/template.js index c0dfcfbb463..0682e289f99 100644 --- a/components/table/stories/template.js +++ b/components/table/stories/template.js @@ -34,6 +34,7 @@ export const TableRowItem = ({ isDropTarget = false, ariaControls, customClasses = [], + cellCustomClasses = {}, } = {}, context = {}) => { const useVisuals = visualElement !== undefined && !isSummaryRow && !isSectionHeader; const useColumnDividers = hasColumnDividers && !isSummaryRow && !isSectionHeader; @@ -112,6 +113,7 @@ export const TableRowItem = ({ [`${rootClass}-cell`]: true, [`${rootClass}-checkboxCell`]: true, [`${rootClass}-cell--alignEnd`]: getTextAlignment(0) === "end", + ...cellCustomClasses?.[0]?.reduce((a, c) => ({ ...a, [c]: true }), {}), })} tabindex="0" > @@ -136,6 +138,7 @@ export const TableRowItem = ({ [`${rootClass}-cell--visual`]: useVisuals, [`${rootClass}-cell--divider`]: useColumnDividers, [`${rootClass}-cell--alignEnd`]: getTextAlignment(0) === "end", + ...cellCustomClasses?.[showCheckbox ? 1 : 0]?.reduce((a, c) => ({ ...a, [c]: true }), {}), })} tabindex="0" > @@ -162,6 +165,7 @@ export const TableRowItem = ({ [`${rootClass}-cell--visual`]: useVisuals, [`${rootClass}-cell--divider`]: useColumnDividers, [`${rootClass}-cell--alignEnd`]: getTextAlignment(0) === "end", + ...cellCustomClasses?.[showCheckbox ? 1 : 0]?.reduce((a, c) => ({ ...a, [c]: true }), {}), })} colspan=${ifDefined(isSectionHeader && showCheckbox ? "4" : isSectionHeader ? "3" : undefined)} tabindex="0" @@ -178,6 +182,7 @@ export const TableRowItem = ({ [`${rootClass}-cell--visual`]: useVisuals, [`${rootClass}-cell--divider`]: useColumnDividers, [`${rootClass}-cell--alignEnd`]: getTextAlignment(1) === "end", + ...cellCustomClasses?.[showCheckbox ? 2 : 1]?.reduce((a, c) => ({ ...a, [c]: true }), {}), })} tabindex="0" >${getCellContent(1)} @@ -188,6 +193,7 @@ export const TableRowItem = ({ [`${rootClass}-cell`]: true, [`${rootClass}-cell--divider`]: useColumnDividers, [`${rootClass}-cell--alignEnd`]: getTextAlignment(2) === "end", + ...cellCustomClasses?.[showCheckbox ? 3 : 2]?.reduce((a, c) => ({ ...a, [c]: true }), {}), })} tabindex="0" >${getCellContent(2)}` From 584ba899555d34a52080eddb3608bce42a599c66 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 21 May 2025 16:45:46 -0400 Subject: [PATCH 15/52] chore(table): update metadata.js --- components/table/dist/metadata.json | 206 ++++++++++------------------ 1 file changed, 73 insertions(+), 133 deletions(-) diff --git a/components/table/dist/metadata.json b/components/table/dist/metadata.json index 26a601ce86e..0b6d2836725 100644 --- a/components/table/dist/metadata.json +++ b/components/table/dist/metadata.json @@ -3,19 +3,10 @@ "selectors": [ ".spectrum-Table", ".spectrum-Table--compact", - ".spectrum-Table--compact.spectrum-Table--sizeL", - ".spectrum-Table--compact.spectrum-Table--sizeS", - ".spectrum-Table--compact.spectrum-Table--sizeXL", ".spectrum-Table--emphasized", ".spectrum-Table--quiet", - ".spectrum-Table--sizeL", - ".spectrum-Table--sizeM", - ".spectrum-Table--sizeS", - ".spectrum-Table--sizeXL", + ".spectrum-Table--quiet .spectrum-Table-headCell", ".spectrum-Table--spacious", - ".spectrum-Table--spacious.spectrum-Table--sizeL", - ".spectrum-Table--spacious.spectrum-Table--sizeS", - ".spectrum-Table--spacious.spectrum-Table--sizeXL", ".spectrum-Table-body", ".spectrum-Table-body .spectrum-Table-row .spectrum-Table-cell:first-child", ".spectrum-Table-body .spectrum-Table-row .spectrum-Table-cell:last-child", @@ -25,18 +16,18 @@ ".spectrum-Table-body.is-drop-target .spectrum-Table-row", ".spectrum-Table-body.is-drop-target .spectrum-Table-row .spectrum-Table-checkbox .spectrum-Checkbox-box:before", ".spectrum-Table-cell", - ".spectrum-Table-cell--alignCenter", - ".spectrum-Table-cell--alignRight", + ".spectrum-Table-cell--alignEnd", + ".spectrum-Table-cell--alignStart", ".spectrum-Table-cell--collapsible", ".spectrum-Table-cell--divider", ".spectrum-Table-cell.is-focused", + ".spectrum-Table-cell.is-focused:after", ".spectrum-Table-cell:focus-visible", + ".spectrum-Table-cell:focus-visible:after", ".spectrum-Table-checkboxCell", ".spectrum-Table-checkboxCell .spectrum-Table-checkbox", - ".spectrum-Table-checkboxCell.spectrum-Table-cell .spectrum-Table-checkbox", ".spectrum-Table-checkboxCell.spectrum-Table-headCell .spectrum-Table-checkbox", ".spectrum-Table-collapseInner", - ".spectrum-Table-collapseInner .spectrum-Table-collapseContent", ".spectrum-Table-columnTitle", ".spectrum-Table-disclosureIcon.spectrum-Table-disclosureIcon", ".spectrum-Table-disclosureIcon.spectrum-Table-disclosureIcon.is-keyboardFocused", @@ -50,22 +41,27 @@ ".spectrum-Table-head", ".spectrum-Table-head [role=\"row\"]", ".spectrum-Table-headCell", - ".spectrum-Table-headCell .spectrum-Table-menuIcon", - ".spectrum-Table-headCell .spectrum-Table-sortedIcon", ".spectrum-Table-headCell.is-focused", - ".spectrum-Table-headCell.is-sortable", + ".spectrum-Table-headCell.is-focused:after", ".spectrum-Table-headCell.is-sortable.is-keyboardFocused", ".spectrum-Table-headCell.is-sortable:active", ".spectrum-Table-headCell.is-sortable:focus", ".spectrum-Table-headCell.is-sortable:focus-visible", ".spectrum-Table-headCell.is-sortable:focus:hover", ".spectrum-Table-headCell.is-sortable:hover", - ".spectrum-Table-headCell.is-sorted-asc .spectrum-Table-sortedIcon", - ".spectrum-Table-headCell.is-sorted-desc .spectrum-Table-sortedIcon", ".spectrum-Table-headCell:focus-visible", + ".spectrum-Table-headCell:focus-visible:after", + ".spectrum-Table-headRow .spectrum-Table-checkboxCell", + ".spectrum-Table-headRow .spectrum-Table-checkboxCell .spectrum-Table-checkbox", + ".spectrum-Table-headRow .spectrum-Table-headCell", + ".spectrum-Table-headRow .spectrum-Table-headCell.is-sortable", + ".spectrum-Table-headRow:first-child .spectrum-Table-headCell:first-child", + ".spectrum-Table-headRow:last-child .spectrum-Table-headCell:last-child", ".spectrum-Table-main", ".spectrum-Table-menuIcon", ".spectrum-Table-row", + ".spectrum-Table-row .is-emphasized", + ".spectrum-Table-row .spectrum-Table-cell", ".spectrum-Table-row--collapsible", ".spectrum-Table-row--collapsible .spectrum-Table-checkboxCell", ".spectrum-Table-row--collapsible .spectrum-Table-disclosureIcon", @@ -83,15 +79,18 @@ ".spectrum-Table-row--sectionHeader:hover", ".spectrum-Table-row--summary", ".spectrum-Table-row--summary .spectrum-Table-cell", - ".spectrum-Table-row--thumbnail", - ".spectrum-Table-row--thumbnail .spectrum-Table-cell", - ".spectrum-Table-row--thumbnail .spectrum-Table-cell--thumbnail", - ".spectrum-Table-row--thumbnail.spectrum-Table-row--collapsible", + ".spectrum-Table-row--visual", + ".spectrum-Table-row--visual .spectrum-Table-cell", + ".spectrum-Table-row--visual .spectrum-Table-cell--visual", + ".spectrum-Table-row--visual.spectrum-Table-row--collapsible", ".spectrum-Table-row.is-drop-target", ".spectrum-Table-row.is-drop-target .spectrum-Table-cell", ".spectrum-Table-row.is-drop-target .spectrum-Table-checkbox .spectrum-Checkbox-box:before", ".spectrum-Table-row.is-focused", + ".spectrum-Table-row.is-focused .spectrum-Table-cell:first-child", + ".spectrum-Table-row.is-focused .spectrum-Table-cell:first-child:before", ".spectrum-Table-row.is-focused .spectrum-Table-checkbox .spectrum-Checkbox-box:before", + ".spectrum-Table-row.is-focused:last-child .spectrum-Table-cell:first-child:before", ".spectrum-Table-row.is-selected", ".spectrum-Table-row.is-selected .spectrum-Table-checkbox .spectrum-Checkbox-box:before", ".spectrum-Table-row.is-selected.is-focused", @@ -99,12 +98,17 @@ ".spectrum-Table-row.is-selected:hover", ".spectrum-Table-row:active", ".spectrum-Table-row:first-child", - ".spectrum-Table-row:first-child .spectrum-Table-cell:first-child", ".spectrum-Table-row:first-child .spectrum-Table-cell:last-child", - ".spectrum-Table-row:focus", + ".spectrum-Table-row:focus .spectrum-Table-cell:first-child", + ".spectrum-Table-row:focus .spectrum-Table-cell:first-child:before", ".spectrum-Table-row:focus-visible", + ".spectrum-Table-row:focus-visible .spectrum-Table-cell:first-child", + ".spectrum-Table-row:focus-visible .spectrum-Table-cell:first-child:before", ".spectrum-Table-row:focus-visible .spectrum-Table-checkbox .spectrum-Checkbox-box:before", + ".spectrum-Table-row:focus-visible:last-child .spectrum-Table-cell:first-child:before", + ".spectrum-Table-row:focus:last-child .spectrum-Table-cell:first-child:before", ".spectrum-Table-row:hover", + ".spectrum-Table-row:hover .spectrum-Table-cell:first-child:before", ".spectrum-Table-row:hover .spectrum-Table-checkbox .spectrum-Checkbox-box:before", ".spectrum-Table-row:last-child", ".spectrum-Table-row:last-child .spectrum-Table-cell:first-child", @@ -115,12 +119,15 @@ ".spectrum-Table-scroller .spectrum-Table-body .spectrum-Table-row:first-child .spectrum-Table-cell", ".spectrum-Table-scroller .spectrum-Table-body .spectrum-Table-row:last-child .spectrum-Table-cell", ".spectrum-Table-scroller .spectrum-Table-head", - ".spectrum-Table-scroller .spectrum-Table-headCell", + ".spectrum-Table-scroller .spectrum-Table-headRow:first-child .spectrum-Table-headCell", ".spectrum-Table-scroller.spectrum-Table--quiet", - ".spectrum-Table-sortedIcon", - ".spectrum-Table-thumbnailInner", - ".spectrum-Table-thumbnailInner .spectrum-Table-thumbnailContent", - ".spectrum-Table-thumbnailInner .spectrum-Thumbnail", + ".spectrum-Table-sortIcon", + ".spectrum-Table-sortIcon:last-child .spectrum-Table-headCell", + ".spectrum-Table-visualInner", + ".spectrum-Table-visualInner .spectrum-Avatar", + ".spectrum-Table-visualInner .spectrum-Icon", + ".spectrum-Table-visualInner .spectrum-Table-visualContent", + ".spectrum-Table-visualInner .spectrum-Thumbnail", ".spectrum-Table:dir(rtl)", ".spectrum-Table:not(.spectrum-Table-scroller)", "[dir=\"rtl\"] .spectrum-Table" @@ -128,10 +135,10 @@ "modifiers": [ "--mod-table-border-color", "--mod-table-border-radius", - "--mod-table-border-radius--quiet", + "--mod-table-border-radius-quiet", "--mod-table-border-width", - "--mod-table-cell-inline-space", - "--mod-table-checkbox-to-text", + "--mod-table-cell-inline-spacing", + "--mod-table-checkbox-to-cell-content", "--mod-table-collapsible-disclosure-inline-spacing", "--mod-table-collapsible-icon-animation-duration", "--mod-table-current-header-height", @@ -144,11 +151,10 @@ "--mod-table-divider-color", "--mod-table-drop-zone-background-color", "--mod-table-drop-zone-outline-color", - "--mod-table-edge-to-content", "--mod-table-focus-indicator-color", "--mod-table-focus-indicator-thickness", "--mod-table-header-background-color", - "--mod-table-header-background-color--quiet", + "--mod-table-header-background-color-quiet", "--mod-table-header-background-color-scrollable", "--mod-table-header-bottom-to-text", "--mod-table-header-checkbox-block-spacing", @@ -156,6 +162,9 @@ "--mod-table-header-font-size", "--mod-table-header-font-weight", "--mod-table-header-line-height", + "--mod-table-header-row-bottom-to-text", + "--mod-table-header-row-checkbox-block-spacing", + "--mod-table-header-row-top-to-text", "--mod-table-header-text-color", "--mod-table-header-text-transform", "--mod-table-header-top-to-text", @@ -166,30 +175,25 @@ "--mod-table-icon-color-focus-hover", "--mod-table-icon-color-hover", "--mod-table-icon-color-key-focus", - "--mod-table-icon-to-text", "--mod-table-min-header-height", "--mod-table-min-row-height", - "--mod-table-min-row-height--compact", - "--mod-table-min-row-height--spacious", + "--mod-table-min-row-height-compact", + "--mod-table-min-row-height-spacious", "--mod-table-outer-border-inline-width", - "--mod-table-outer-border-inline-width--quiet", + "--mod-table-outer-border-inline-width-quiet", "--mod-table-row-active-color", "--mod-table-row-background-color", - "--mod-table-row-background-color--quiet", "--mod-table-row-background-color-hover", + "--mod-table-row-background-color-quiet", "--mod-table-row-bottom-to-text", - "--mod-table-row-bottom-to-text--compact", - "--mod-table-row-bottom-to-text--spacious", "--mod-table-row-checkbox-block-spacing", - "--mod-table-row-checkbox-block-spacing--compact", - "--mod-table-row-checkbox-block-spacing--spacious", + "--mod-table-row-checkbox-block-spacing-compact", + "--mod-table-row-checkbox-block-spacing-spacious", "--mod-table-row-font-size", "--mod-table-row-font-weight", "--mod-table-row-line-height", "--mod-table-row-text-color", "--mod-table-row-top-to-text", - "--mod-table-row-top-to-text--compact", - "--mod-table-row-top-to-text--spacious", "--mod-table-section-header-background-color", "--mod-table-section-header-block-end-spacing", "--mod-table-section-header-block-start-spacing", @@ -197,6 +201,7 @@ "--mod-table-section-header-font-size", "--mod-table-section-header-font-style", "--mod-table-section-header-font-weight", + "--mod-table-section-header-inline-start-spacing", "--mod-table-section-header-line-height", "--mod-table-section-header-min-height", "--mod-table-section-header-text-color", @@ -207,12 +212,15 @@ "--mod-table-sort-icon-inline-end-spacing", "--mod-table-sort-icon-inline-start-spacing", "--mod-table-summary-row-background-color", + "--mod-table-summary-row-bottom-to-text", "--mod-table-summary-row-font-family", "--mod-table-summary-row-font-size", "--mod-table-summary-row-font-style", "--mod-table-summary-row-font-weight", "--mod-table-summary-row-line-height", + "--mod-table-summary-row-min-height", "--mod-table-summary-row-text-color", + "--mod-table-summary-row-top-to-text", "--mod-table-thumbnail-block-spacing", "--mod-table-thumbnail-block-spacing-compact", "--mod-table-thumbnail-block-spacing-spacious", @@ -220,7 +228,8 @@ "--mod-table-thumbnail-size-compact", "--mod-table-thumbnail-size-spacious", "--mod-table-thumbnail-to-text", - "--mod-table-transition-duration" + "--mod-table-transition-duration", + "--mod-table-visual-to-text" ], "component": [ "--spectrum-table-border-color", @@ -228,19 +237,14 @@ "--spectrum-table-border-radius", "--spectrum-table-border-width", "--spectrum-table-cell-background-color", - "--spectrum-table-cell-inline-space", + "--spectrum-table-cell-inline-spacing", + "--spectrum-table-checkbox-to-cell-content", "--spectrum-table-checkbox-to-text", "--spectrum-table-collapsible-disclosure-inline-spacing", "--spectrum-table-collapsible-icon-animation-duration", "--spectrum-table-collapsible-tier-indent", - "--spectrum-table-column-header-row-bottom-to-text-extra-large", - "--spectrum-table-column-header-row-bottom-to-text-large", "--spectrum-table-column-header-row-bottom-to-text-medium", - "--spectrum-table-column-header-row-bottom-to-text-small", - "--spectrum-table-column-header-row-top-to-text-extra-large", - "--spectrum-table-column-header-row-top-to-text-large", "--spectrum-table-column-header-row-top-to-text-medium", - "--spectrum-table-column-header-row-top-to-text-small", "--spectrum-table-default-vertical-align", "--spectrum-table-disclosure-icon-size", "--spectrum-table-divider-color", @@ -253,13 +257,12 @@ "--spectrum-table-header-bottom-to-text", "--spectrum-table-header-checkbox-block-spacing", "--spectrum-table-header-font-weight", - "--spectrum-table-header-row-checkbox-to-top-extra-large", - "--spectrum-table-header-row-checkbox-to-top-large", + "--spectrum-table-header-row-bottom-to-text", + "--spectrum-table-header-row-checkbox-block-spacing", "--spectrum-table-header-row-checkbox-to-top-medium", - "--spectrum-table-header-row-checkbox-to-top-small", + "--spectrum-table-header-row-top-to-text", "--spectrum-table-header-text-color", "--spectrum-table-header-top-to-text", - "--spectrum-table-header-vertical-align", "--spectrum-table-icon-color", "--spectrum-table-icon-color-active", "--spectrum-table-icon-color-default", @@ -267,82 +270,43 @@ "--spectrum-table-icon-color-focus-hover", "--spectrum-table-icon-color-hover", "--spectrum-table-icon-color-key-focus", - "--spectrum-table-icon-to-text", - "--spectrum-table-min-header-height", + "--spectrum-table-min-header-row-height", "--spectrum-table-min-row-height", "--spectrum-table-outer-border-inline-width", "--spectrum-table-row-active-color", "--spectrum-table-row-background-color", "--spectrum-table-row-background-color-hover", "--spectrum-table-row-bottom-to-text", - "--spectrum-table-row-bottom-to-text-extra-large-compact", - "--spectrum-table-row-bottom-to-text-extra-large-regular", - "--spectrum-table-row-bottom-to-text-extra-large-spacious", - "--spectrum-table-row-bottom-to-text-large-compact", - "--spectrum-table-row-bottom-to-text-large-regular", - "--spectrum-table-row-bottom-to-text-large-spacious", + "--spectrum-table-row-bottom-to-text-medium", "--spectrum-table-row-bottom-to-text-medium-compact", - "--spectrum-table-row-bottom-to-text-medium-regular", "--spectrum-table-row-bottom-to-text-medium-spacious", - "--spectrum-table-row-bottom-to-text-small-compact", - "--spectrum-table-row-bottom-to-text-small-regular", - "--spectrum-table-row-bottom-to-text-small-spacious", "--spectrum-table-row-checkbox-block-spacing", - "--spectrum-table-row-checkbox-to-top-extra-large-compact", - "--spectrum-table-row-checkbox-to-top-extra-large-regular", - "--spectrum-table-row-checkbox-to-top-extra-large-spacious", - "--spectrum-table-row-checkbox-to-top-large-compact", - "--spectrum-table-row-checkbox-to-top-large-regular", - "--spectrum-table-row-checkbox-to-top-large-spacious", + "--spectrum-table-row-checkbox-to-top-medium", "--spectrum-table-row-checkbox-to-top-medium-compact", - "--spectrum-table-row-checkbox-to-top-medium-regular", "--spectrum-table-row-checkbox-to-top-medium-spacious", - "--spectrum-table-row-checkbox-to-top-small-compact", - "--spectrum-table-row-checkbox-to-top-small-regular", - "--spectrum-table-row-checkbox-to-top-small-spacious", "--spectrum-table-row-down-opacity", + "--spectrum-table-row-focus-indicator-width", "--spectrum-table-row-font-family", "--spectrum-table-row-font-size", "--spectrum-table-row-font-style", "--spectrum-table-row-font-weight", - "--spectrum-table-row-height-extra-large-compact", - "--spectrum-table-row-height-extra-large-regular", - "--spectrum-table-row-height-extra-large-spacious", - "--spectrum-table-row-height-large-compact", - "--spectrum-table-row-height-large-regular", - "--spectrum-table-row-height-large-spacious", + "--spectrum-table-row-height-medium", "--spectrum-table-row-height-medium-compact", - "--spectrum-table-row-height-medium-regular", "--spectrum-table-row-height-medium-spacious", - "--spectrum-table-row-height-small-compact", - "--spectrum-table-row-height-small-regular", - "--spectrum-table-row-height-small-spacious", "--spectrum-table-row-hover-opacity", "--spectrum-table-row-line-height", "--spectrum-table-row-text-color", "--spectrum-table-row-tier", "--spectrum-table-row-top-to-text", - "--spectrum-table-row-top-to-text-extra-large-compact", - "--spectrum-table-row-top-to-text-extra-large-regular", - "--spectrum-table-row-top-to-text-extra-large-spacious", - "--spectrum-table-row-top-to-text-large-compact", - "--spectrum-table-row-top-to-text-large-regular", - "--spectrum-table-row-top-to-text-large-spacious", + "--spectrum-table-row-top-to-text-medium", "--spectrum-table-row-top-to-text-medium-compact", - "--spectrum-table-row-top-to-text-medium-regular", "--spectrum-table-row-top-to-text-medium-spacious", - "--spectrum-table-row-top-to-text-small-compact", - "--spectrum-table-row-top-to-text-small-regular", - "--spectrum-table-row-top-to-text-small-spacious", "--spectrum-table-section-header-background-color", "--spectrum-table-section-header-block-end-spacing", "--spectrum-table-section-header-block-start-spacing", "--spectrum-table-section-header-font-weight", "--spectrum-table-section-header-min-height", - "--spectrum-table-section-header-row-height-extra-large", - "--spectrum-table-section-header-row-height-large", "--spectrum-table-section-header-row-height-medium", - "--spectrum-table-section-header-row-height-small", "--spectrum-table-selected-cell-background-color", "--spectrum-table-selected-cell-background-color-focus", "--spectrum-table-selected-row-background-color", @@ -361,19 +325,12 @@ "--spectrum-table-thumbnail-inner-minimum-block-spacing", "--spectrum-table-thumbnail-size", "--spectrum-table-thumbnail-to-text", - "--spectrum-table-thumbnail-to-top-minimum-extra-large-compact", - "--spectrum-table-thumbnail-to-top-minimum-extra-large-regular", - "--spectrum-table-thumbnail-to-top-minimum-extra-large-spacious", - "--spectrum-table-thumbnail-to-top-minimum-large-compact", - "--spectrum-table-thumbnail-to-top-minimum-large-regular", - "--spectrum-table-thumbnail-to-top-minimum-large-spacious", "--spectrum-table-thumbnail-to-top-minimum-medium-compact", "--spectrum-table-thumbnail-to-top-minimum-medium-regular", "--spectrum-table-thumbnail-to-top-minimum-medium-spacious", - "--spectrum-table-thumbnail-to-top-minimum-small-compact", - "--spectrum-table-thumbnail-to-top-minimum-small-regular", - "--spectrum-table-thumbnail-to-top-minimum-small-spacious", - "--spectrum-table-transition-duration" + "--spectrum-table-transition-duration", + "--spectrum-table-visual-inner-content-block-spacing", + "--spectrum-table-visual-to-text" ], "global": [ "--spectrum-accent-visual-color", @@ -384,32 +341,19 @@ "--spectrum-bold-font-weight", "--spectrum-checkbox-control-size-small", "--spectrum-component-bottom-to-text-100", - "--spectrum-component-bottom-to-text-200", - "--spectrum-component-bottom-to-text-300", - "--spectrum-component-bottom-to-text-75", "--spectrum-component-height-100", - "--spectrum-component-height-200", - "--spectrum-component-height-300", - "--spectrum-component-height-75", "--spectrum-component-top-to-text-100", - "--spectrum-component-top-to-text-200", - "--spectrum-component-top-to-text-300", - "--spectrum-component-top-to-text-75", - "--spectrum-corner-radius-100", + "--spectrum-corner-radius-medium-size-extra-small", "--spectrum-default-font-style", "--spectrum-drop-zone-background-color-opacity", "--spectrum-drop-zone-background-color-rgb", "--spectrum-focus-indicator-color", "--spectrum-focus-indicator-thickness", "--spectrum-font-size-100", - "--spectrum-font-size-200", - "--spectrum-font-size-300", - "--spectrum-font-size-75", "--spectrum-gray-100", "--spectrum-gray-200", "--spectrum-gray-25", "--spectrum-gray-300", - "--spectrum-gray-50", "--spectrum-gray-700-rgb", "--spectrum-gray-75", "--spectrum-gray-900-rgb", @@ -422,20 +366,16 @@ "--spectrum-neutral-subdued-content-color-key-focus", "--spectrum-regular-font-weight", "--spectrum-sans-font-family-stack", + "--spectrum-side-focus-indicator", "--spectrum-spacing-300", - "--spectrum-text-to-visual-100", - "--spectrum-text-to-visual-200", "--spectrum-text-to-visual-300", "--spectrum-thumbnail-size-200", "--spectrum-thumbnail-size-300", - "--spectrum-thumbnail-size-50", "--spectrum-thumbnail-size-500", - "--spectrum-thumbnail-size-700", - "--spectrum-thumbnail-size-800", "--spectrum-transparent-white-100", "--spectrum-transparent-white-25" ], - "passthroughs": ["--mod-checkbox-spacing", "--mod-thumbnail-size"], + "passthroughs": ["--mod-checkbox-margin-block", "--mod-thumbnail-size"], "high-contrast": [ "--highcontrast-table-border-color", "--highcontrast-table-cell-focus-extra-offset", From 5350bdf86f9724a0f90d843c7e2a86ef55ae7fbb Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 21 May 2025 17:48:58 -0400 Subject: [PATCH 16/52] fix(table): linting errors fixed - addresses some unused or "undefined" custom properties related to thumbnail that are not in use anymore - updates metadata - passes density arg to table row template to appropriately size the visuals --- components/table/dist/metadata.json | 38 ++++++++----------- components/table/index.css | 56 ++++++++-------------------- components/table/stories/template.js | 18 +++++++-- 3 files changed, 45 insertions(+), 67 deletions(-) diff --git a/components/table/dist/metadata.json b/components/table/dist/metadata.json index 0b6d2836725..f53a530b6fd 100644 --- a/components/table/dist/metadata.json +++ b/components/table/dist/metadata.json @@ -79,10 +79,6 @@ ".spectrum-Table-row--sectionHeader:hover", ".spectrum-Table-row--summary", ".spectrum-Table-row--summary .spectrum-Table-cell", - ".spectrum-Table-row--visual", - ".spectrum-Table-row--visual .spectrum-Table-cell", - ".spectrum-Table-row--visual .spectrum-Table-cell--visual", - ".spectrum-Table-row--visual.spectrum-Table-row--collapsible", ".spectrum-Table-row.is-drop-target", ".spectrum-Table-row.is-drop-target .spectrum-Table-cell", ".spectrum-Table-row.is-drop-target .spectrum-Table-checkbox .spectrum-Checkbox-box:before", @@ -126,13 +122,15 @@ ".spectrum-Table-visualInner", ".spectrum-Table-visualInner .spectrum-Avatar", ".spectrum-Table-visualInner .spectrum-Icon", - ".spectrum-Table-visualInner .spectrum-Table-visualContent", ".spectrum-Table-visualInner .spectrum-Thumbnail", ".spectrum-Table:dir(rtl)", ".spectrum-Table:not(.spectrum-Table-scroller)", "[dir=\"rtl\"] .spectrum-Table" ], "modifiers": [ + "--mod-table-avatar-size", + "--mod-table-avatar-size-compact", + "--mod-table-avatar-size-spacious", "--mod-table-border-color", "--mod-table-border-radius", "--mod-table-border-radius-quiet", @@ -221,17 +219,14 @@ "--mod-table-summary-row-min-height", "--mod-table-summary-row-text-color", "--mod-table-summary-row-top-to-text", - "--mod-table-thumbnail-block-spacing", - "--mod-table-thumbnail-block-spacing-compact", - "--mod-table-thumbnail-block-spacing-spacious", "--mod-table-thumbnail-size", "--mod-table-thumbnail-size-compact", "--mod-table-thumbnail-size-spacious", - "--mod-table-thumbnail-to-text", "--mod-table-transition-duration", "--mod-table-visual-to-text" ], "component": [ + "--spectrum-table-avatar-size", "--spectrum-table-border-color", "--spectrum-table-border-divider-width", "--spectrum-table-border-radius", @@ -255,7 +250,6 @@ "--spectrum-table-focus-indicator-thickness", "--spectrum-table-header-background-color", "--spectrum-table-header-bottom-to-text", - "--spectrum-table-header-checkbox-block-spacing", "--spectrum-table-header-font-weight", "--spectrum-table-header-row-bottom-to-text", "--spectrum-table-header-row-checkbox-block-spacing", @@ -319,22 +313,16 @@ "--spectrum-table-selected-row-background-opacity-non-emphasized-hover", "--spectrum-table-summary-row-background-color", "--spectrum-table-summary-row-font-weight", - "--spectrum-table-thumbnail-block-spacing", - "--spectrum-table-thumbnail-cell-block-spacing", - "--spectrum-table-thumbnail-inner-content-block-spacing", - "--spectrum-table-thumbnail-inner-minimum-block-spacing", "--spectrum-table-thumbnail-size", - "--spectrum-table-thumbnail-to-text", - "--spectrum-table-thumbnail-to-top-minimum-medium-compact", - "--spectrum-table-thumbnail-to-top-minimum-medium-regular", - "--spectrum-table-thumbnail-to-top-minimum-medium-spacious", "--spectrum-table-transition-duration", - "--spectrum-table-visual-inner-content-block-spacing", "--spectrum-table-visual-to-text" ], "global": [ "--spectrum-accent-visual-color", "--spectrum-animation-duration-100", + "--spectrum-avatar-size-100", + "--spectrum-avatar-size-50", + "--spectrum-avatar-size-75", "--spectrum-background-layer-1-color", "--spectrum-blue-900-rgb", "--spectrum-body-color", @@ -369,16 +357,20 @@ "--spectrum-side-focus-indicator", "--spectrum-spacing-300", "--spectrum-text-to-visual-300", + "--spectrum-thumbnail-size-100", "--spectrum-thumbnail-size-200", - "--spectrum-thumbnail-size-300", - "--spectrum-thumbnail-size-500", + "--spectrum-thumbnail-size-75", "--spectrum-transparent-white-100", "--spectrum-transparent-white-25" ], - "passthroughs": ["--mod-checkbox-margin-block", "--mod-thumbnail-size"], + "passthroughs": [ + "--mod-avatar-block-size", + "--mod-avatar-inline-size", + "--mod-checkbox-margin-block", + "--mod-thumbnail-size" + ], "high-contrast": [ "--highcontrast-table-border-color", - "--highcontrast-table-cell-focus-extra-offset", "--highcontrast-table-cell-focus-indicator-color", "--highcontrast-table-divider-color", "--highcontrast-table-focus-indicator-color", diff --git a/components/table/index.css b/components/table/index.css index 48c2c4fa153..5a8bc8059c6 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -95,9 +95,9 @@ --spectrum-table-section-header-block-start-spacing: var(--spectrum-component-top-to-text-100); --spectrum-table-section-header-block-end-spacing: var(--spectrum-component-bottom-to-text-100); - --spectrum-table-thumbnail-block-spacing: var(--spectrum-table-thumbnail-to-top-minimum-medium-regular); --spectrum-table-visual-to-text: var(--spectrum-text-to-visual-300); - --spectrum-table-thumbnail-size: var(--spectrum-thumbnail-size-300); + --spectrum-table-thumbnail-size: var(--spectrum-thumbnail-size-100); + --spectrum-table-avatar-size: var(--spectrum-avatar-size-75); --spectrum-table-cell-inline-spacing: var(--spectrum-table-edge-to-content); --spectrum-table-checkbox-to-cell-content: var(--spectrum-table-checkbox-to-text); @@ -108,6 +108,8 @@ /* @passthrough */ --mod-thumbnail-size: var(--mod-table-thumbnail-size, var(--spectrum-table-thumbnail-size)); + --mod-avatar-block-size: var(--mod-table-avatar-size, var(--spectrum-table-avatar-size)); + --mod-avatar-inline-size: var(--mod-table-avatar-size, var(--spectrum-table-avatar-size)); &:dir(rtl) { --spectrum-logical-rotation: matrix(-1, 0, 0, 1, 0, 0); @@ -124,9 +126,9 @@ /* Row Selection */ --spectrum-table-row-checkbox-block-spacing: var(--mod-table-row-checkbox-block-spacing-compact, var(--spectrum-table-row-checkbox-to-top-medium-compact)); - /* Collapsible and Thumbnails */ - --spectrum-table-thumbnail-block-spacing: var(--mod-table-thumbnail-block-spacing-compact, var(--spectrum-table-thumbnail-to-top-minimum-medium-compact)); - --mod-table-thumbnail-size: var(--mod-table-thumbnail-size-compact, var(--spectrum-thumbnail-size-200)); + /* Visuals */ + --mod-table-thumbnail-size: var(--mod-table-thumbnail-size-compact, var(--spectrum-thumbnail-size-75)); + --mod-table-avatar-size: var(--mod-table-avatar-size-compact, var(--spectrum-avatar-size-50)); } .spectrum-Table--spacious { @@ -138,9 +140,9 @@ /* Row Selection */ --spectrum-table-row-checkbox-block-spacing: var(--mod-table-row-checkbox-block-spacing-spacious, var(--spectrum-table-row-checkbox-to-top-medium-spacious)); - /* Collapsible and Thumbnails */ - --spectrum-table-thumbnail-block-spacing: var(--mod-table-thumbnail-block-spacing-spacious, var(--spectrum-table-thumbnail-to-top-minimum-medium-spacious)); - --mod-table-thumbnail-size: var(--mod-table-thumbnail-size-spacious, var(--spectrum-thumbnail-size-500)); + /* Visuals */ + --mod-table-thumbnail-size: var(--mod-table-thumbnail-size-spacious, var(--spectrum-thumbnail-size-200)); + --mod-table-avatar-size: var(--mod-table-avatar-size-spacious, var(--spectrum-avatar-size-100)); } .spectrum-Table--emphasized, @@ -494,7 +496,7 @@ inset-block-start: 0; inset-block-end: 0; inset-inline-start: 0; - inline-size: var(--spectrum-side-focus-indicator); + inline-size: var(--spectrum-table-row-focus-indicator-width); background-color: var(--spectrum-table-focus-indicator-color); z-index: 1; } @@ -581,8 +583,8 @@ } &.spectrum-Table-headCell .spectrum-Table-checkbox { - margin-block-start: calc(var(--mod-table-header-checkbox-block-spacing, var(--spectrum-table-header-checkbox-block-spacing)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); - margin-block-end: var(--mod-table-header-checkbox-block-spacing, var(--spectrum-table-header-checkbox-block-spacing)); + margin-block-start: calc(var(--mod-table-header-checkbox-block-spacing, var(--spectrum-table-header-row-checkbox-block-spacing)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); + margin-block-end: var(--mod-table-header-checkbox-block-spacing, var(--spectrum-table-header-row-checkbox-block-spacing)); } } @@ -718,6 +720,8 @@ } .spectrum-Table-disclosureIcon.spectrum-Table-disclosureIcon { + --spectrum-table-icon-color: var(--highcontrast-table-icon-color, var(--mod-table-icon-color-default, var(--spectrum-table-icon-color-default))); + block-size: var(--mod-table-disclosure-icon-size, var(--spectrum-table-disclosure-icon-size)); flex-basis: var(--mod-table-disclosure-icon-size, var(--spectrum-table-disclosure-icon-size)); flex-grow: 0; @@ -767,27 +771,6 @@ } /********* VISUALS- THUMBNAILS, AVATARS, ICONS *********/ -.spectrum-Table-row--visual { - --spectrum-table-thumbnail-cell-block-spacing: var(--mod-table-thumbnail-block-spacing, var(--spectrum-table-thumbnail-block-spacing)); - --spectrum-table-thumbnail-inner-content-block-spacing: max(0px, calc((var(--mod-table-thumbnail-size, var(--spectrum-table-thumbnail-size)) - (var(--mod-table-row-line-height, var(--spectrum-table-row-line-height)) * var(--mod-table-header-font-size, var(--spectrum-table-row-font-size)))) / 2)); - - /* Cell that does not have a thumbnail, within a row that has a thumbnail. */ - .spectrum-Table-cell { - padding-block: calc(var(--spectrum-table-thumbnail-cell-block-spacing) + var(--spectrum-table-thumbnail-inner-content-block-spacing)); - } - - /* Cell that does have a thumbnail has its padding moved within the "Inner" and "Content" alignment wrappers. */ - .spectrum-Table-cell--visual { - padding-block: 0; - } - - /* Sometimes the padding must be larger to match any extra space created by the disclosure icon (e.g. compact + large ). */ - &.spectrum-Table-row--collapsible { - --spectrum-table-thumbnail-inner-minimum-block-spacing: max(0px, calc((var(--mod-table-disclosure-icon-size, var(--spectrum-table-disclosure-icon-size)) - var(--mod-table-thumbnail-size, var(--spectrum-table-thumbnail-size))) / 2)); - --spectrum-table-thumbnail-cell-block-spacing: max(var(--mod-table-thumbnail-block-spacing, var(--spectrum-table-thumbnail-block-spacing)), var(--spectrum-table-thumbnail-inner-minimum-block-spacing)); - } -} - .spectrum-Table-visualInner { display: flex; flex-direction: row; @@ -795,21 +778,15 @@ justify-content: flex-start; align-items: center; gap: var(--spectrum-table-visual-to-text); - padding-block: var(--spectrum-table-thumbnail-cell-block-spacing); .spectrum-Thumbnail, .spectrum-Icon, .spectrum-Avatar { flex-grow: 0; flex-shrink: 0; - margin-inline-end: var(--mod-table-thumbnail-to-text, var(--spectrum-table-thumbnail-to-text)); + margin-inline-end: var(--mod-table-visual-to-text, var(--spectrum-table-visual-to-text)); line-height: var(--mod-table-row-line-height, var(--spectrum-table-row-line-height)); } - - .spectrum-Table-visualContent { - /* Vertically centers text with the middle of the cell with the visual. */ - padding-block: var(--spectrum-table-visual-inner-content-block-spacing); - } } /********* HIGH CONTRAST *********/ @@ -874,7 +851,6 @@ .spectrum-Table-body.is-drop-target .spectrum-Table-row { /* Ensure negative offset outline contrasts on top of SelectedItem background. */ --highcontrast-table-cell-focus-indicator-color: var(--highcontrast-table-selected-row-text-color); - --highcontrast-table-cell-focus-extra-offset: 1px; .spectrum-Table-checkbox .spectrum-Checkbox-box::before { outline: var(--highcontrast-table-selected-row-text-color) 1px solid; diff --git a/components/table/stories/template.js b/components/table/stories/template.js index 0682e289f99..e5acb53b783 100644 --- a/components/table/stories/template.js +++ b/components/table/stories/template.js @@ -26,6 +26,7 @@ export const TableRowItem = ({ isExpanded = false, isHidden = false, hasColumnDividers = false, + density = "regular", tier, isLastTier = false, useDivs = false, @@ -50,18 +51,26 @@ export const TableRowItem = ({ ? cellContent[columnIndex] : cellContent; - if (useVisuals) { + if (useVisuals && columnIndex < 2) { return html`
${visualElement === "thumbnail" ? Thumbnail({ - size: "75", + size: ({ + compact: "75", + regular: "100", + spacious: "200", + }[density] || "100"), imageURL: "example-card-landscape.png", isCover: true, }, context) : visualElement === "avatar" ? Avatar({ - size: "75", + size: ({ + compact: "50", + regular: "75", + spacious: "100", + }[density] || "75"), imageURL: "example-card-landscape.png", isCover: true, }, context) @@ -204,7 +213,7 @@ export const TableRowItem = ({ export const Template = ({ rootClass = "spectrum-Table", - density = "standard", + density = "regular", isQuiet = false, isEmphasized = true, isLoading = false, @@ -355,6 +364,7 @@ export const Template = ({ ${rowItems.map((item) => TableRowItem({ rootClass, + density, useDivs, visualElement, hasColumnDividers, From 08b0d154b23d6419854b4a66a592f2a0de8a0067 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 22 May 2025 13:54:59 -0400 Subject: [PATCH 17/52] docs(table): remove collapsible variants from autodocs --- components/table/stories/table.stories.js | 58 +++++++++++++---------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index 01693dfd8b7..206889f0a28 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -388,7 +388,7 @@ QuietMultiSelect.parameters = { /** * Column dividers are for organizing table content and aid the user in parsing related data. These are optional; use them carefully to group related content. * - * The standard table can display column dividers by including the `.spectrum-Table-cell--divider` class with `.spectrum-Table-cell`. Use sparingly to group related content. + * The standard table can display column dividers by including the `.spectrum-Table-cell--divider` class with `.spectrum-Table-cell`. */ export const WithColumnDividers = Template.bind({}); WithColumnDividers.args = { @@ -534,6 +534,34 @@ DivsScrollable.parameters = { chromatic: { disableSnapshot: true }, }; + +/** + * Thumbnails, avatars, and other icons can be used in the table content, with some additional markup and classes for alignment. + */ +export const Visuals = Template.bind({}); +Visuals.args = { + rowItems: [ + { + cellContent: "Avatar example", + visualElement: "avatar", + }, + { + cellContent: "Icon example", + visualElement: "icon", + }, + { + cellContent: "Thumbnail example", + visualElement: "thumbnail", + }, + ], +}; +Visuals.parameters = { + chromatic: { disableSnapshot: true }, +}; +Visuals.storyName = "With visuals"; + + +// TODO: The design team doesn't have support for collapsible rows in the table component, so they are removed from the docs page for now. /** * In a table with collapsible rows, all child items must have columns that match the parent items. If they don’t, consider using multiple drill-in tables, [Miller columns](/docs/components-miller-columns--docs), or [breadcrumbs](/docs/components-breadcrumbs--docs) instead. */ @@ -600,6 +628,7 @@ Collapsible.parameters = { chromatic: { disableSnapshot: true }, }; Collapsible.storyName = "Collapsible rows"; +Collapsible.tags = ["!autodocs", "!dev"]; export const CollapsibleMultiSelect = Template.bind({}); CollapsibleMultiSelect.storyName = "Selection mode: multiple, collapsible rows"; @@ -672,31 +701,7 @@ CollapsibleMultiSelect.args = { CollapsibleMultiSelect.parameters = { chromatic: { disableSnapshot: true }, }; - -/** - * Thumbnails, avatars, and other icons can be used in the table content, with some additional markup and classes for alignment. - */ -export const Visuals = Template.bind({}); -Visuals.args = { - rowItems: [ - { - cellContent: "Avatar example", - visualElement: "avatar", - }, - { - cellContent: "Icon example", - visualElement: "icon", - }, - { - cellContent: "Thumbnail example", - visualElement: "thumbnail", - }, - ], -}; -Visuals.parameters = { - chromatic: { disableSnapshot: true }, -}; -Visuals.storyName = "With visuals"; +CollapsibleMultiSelect.tags = ["!autodocs", "!dev"]; /** * The table can also combine visuals with collapsible rows. @@ -763,6 +768,7 @@ VisualsCollapsible.args = { }, ], }; +VisualsCollapsible.tags = ["!autodocs", "!dev"]; VisualsCollapsible.storyName = "With visuals: collapsible rows"; VisualsCollapsible.parameters = { chromatic: { disableSnapshot: true }, From 488ef54161dcf35e08a9c68cfd48dc75d644ecfc Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 22 May 2025 13:57:06 -0400 Subject: [PATCH 18/52] fix(table): adds extra block borders for row focus --- components/table/dist/metadata.json | 14 ++++++++++++++ components/table/index.css | 28 ++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/components/table/dist/metadata.json b/components/table/dist/metadata.json index f53a530b6fd..bf4a2130177 100644 --- a/components/table/dist/metadata.json +++ b/components/table/dist/metadata.json @@ -83,9 +83,13 @@ ".spectrum-Table-row.is-drop-target .spectrum-Table-cell", ".spectrum-Table-row.is-drop-target .spectrum-Table-checkbox .spectrum-Checkbox-box:before", ".spectrum-Table-row.is-focused", + ".spectrum-Table-row.is-focused .spectrum-Table-cell", ".spectrum-Table-row.is-focused .spectrum-Table-cell:first-child", ".spectrum-Table-row.is-focused .spectrum-Table-cell:first-child:before", + ".spectrum-Table-row.is-focused .spectrum-Table-cell:last-child", ".spectrum-Table-row.is-focused .spectrum-Table-checkbox .spectrum-Checkbox-box:before", + ".spectrum-Table-row.is-focused:first-child .spectrum-Table-cell", + ".spectrum-Table-row.is-focused:last-child .spectrum-Table-cell", ".spectrum-Table-row.is-focused:last-child .spectrum-Table-cell:first-child:before", ".spectrum-Table-row.is-selected", ".spectrum-Table-row.is-selected .spectrum-Table-checkbox .spectrum-Checkbox-box:before", @@ -95,13 +99,22 @@ ".spectrum-Table-row:active", ".spectrum-Table-row:first-child", ".spectrum-Table-row:first-child .spectrum-Table-cell:last-child", + ".spectrum-Table-row:focus", + ".spectrum-Table-row:focus .spectrum-Table-cell", ".spectrum-Table-row:focus .spectrum-Table-cell:first-child", ".spectrum-Table-row:focus .spectrum-Table-cell:first-child:before", + ".spectrum-Table-row:focus .spectrum-Table-cell:last-child", ".spectrum-Table-row:focus-visible", + ".spectrum-Table-row:focus-visible .spectrum-Table-cell", ".spectrum-Table-row:focus-visible .spectrum-Table-cell:first-child", ".spectrum-Table-row:focus-visible .spectrum-Table-cell:first-child:before", + ".spectrum-Table-row:focus-visible .spectrum-Table-cell:last-child", ".spectrum-Table-row:focus-visible .spectrum-Table-checkbox .spectrum-Checkbox-box:before", + ".spectrum-Table-row:focus-visible:first-child .spectrum-Table-cell", + ".spectrum-Table-row:focus-visible:last-child .spectrum-Table-cell", ".spectrum-Table-row:focus-visible:last-child .spectrum-Table-cell:first-child:before", + ".spectrum-Table-row:focus:first-child .spectrum-Table-cell", + ".spectrum-Table-row:focus:last-child .spectrum-Table-cell", ".spectrum-Table-row:focus:last-child .spectrum-Table-cell:first-child:before", ".spectrum-Table-row:hover", ".spectrum-Table-row:hover .spectrum-Table-cell:first-child:before", @@ -279,6 +292,7 @@ "--spectrum-table-row-checkbox-to-top-medium-compact", "--spectrum-table-row-checkbox-to-top-medium-spacious", "--spectrum-table-row-down-opacity", + "--spectrum-table-row-focus-indicator-outline-width", "--spectrum-table-row-focus-indicator-width", "--spectrum-table-row-font-family", "--spectrum-table-row-font-size", diff --git a/components/table/index.css b/components/table/index.css index 5a8bc8059c6..dfc530a7d89 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -49,6 +49,7 @@ --spectrum-table-focus-indicator-thickness: var(--spectrum-focus-indicator-thickness); --spectrum-table-focus-indicator-color: var(--spectrum-focus-indicator-color); --spectrum-table-row-focus-indicator-width: var(--spectrum-side-focus-indicator); + --spectrum-table-row-focus-indicator-outline-width: 1px; --spectrum-table-selected-cell-background-color-focus: var(--highcontrast-table-selected-row-background-color-focus, var(--mod-table-selected-row-background-color-non-emphasized-focus, var(--spectrum-table-selected-row-background-color-non-emphasized-focus))); @@ -485,11 +486,15 @@ &.is-focused, &:focus-visible, &:focus { + outline: none; + + /* First cell of focused rows need fancy new focus indicator borders & no border-radius, and the thicker row focus indicator */ .spectrum-Table-cell:first-child { position: relative; - overflow: hidden; - outline: none; + border-inline-start: 0; + border-block: var(--spectrum-table-row-focus-indicator-outline-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); + /* The side row focus indicator line */ &::before { content: ""; position: absolute; @@ -502,6 +507,20 @@ } } + /* All other cells in focused rows (including the first rows after the header row and last rows in the table) need the focus indicator border */ + &:first-child .spectrum-Table-cell, + &:last-child .spectrum-Table-cell, + .spectrum-Table-cell { + border-block: var(--spectrum-table-row-focus-indicator-outline-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); + } + + /* Last cell of focused row needs the fancy new focus indicator borders at the end */ + .spectrum-Table-cell:last-child { + border-inline-end: var(--spectrum-table-row-focus-indicator-outline-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); + border-block-end: var(--spectrum-table-row-focus-indicator-outline-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); + } + + /* In a focused row that is last in the table, the first cell needs rounded corners at the bottom of the row focus indicator line */ &:last-child .spectrum-Table-cell:first-child::before { border-end-start-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); } @@ -843,6 +862,11 @@ .spectrum-Table-cell:first-child::before { background-color: Highlight; } + + /* Make sure border colors are visible in high contrast mode */ + /* .spectrum-Table-cell { + border-color: Highlight; + } */ } } From 957a3c9c0f6c5c2330aa04969e35073d6590385a Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 22 May 2025 14:25:09 -0400 Subject: [PATCH 19/52] chore(table): expands test coverage - fixes up existing tests to use new default props if necessary - adds numerical test, text alignment, visual elements, empty state, loading state, and a test of a smattering of other focus/selected tests for the rows and cells --- components/table/stories/table.test.js | 130 ++++++++++++++++++++----- 1 file changed, 107 insertions(+), 23 deletions(-) diff --git a/components/table/stories/table.test.js b/components/table/stories/table.test.js index 73022af599f..a65ad40ed4b 100644 --- a/components/table/stories/table.test.js +++ b/components/table/stories/table.test.js @@ -133,17 +133,25 @@ export const TableGroup = Variants({ hasColumnDividers: true, }, { - testHeading: "Summary: selected", + testHeading: "Empty state", + rowItems: [], + }, + { + testHeading: "Loading state", + isLoading: true, + }, + { + testHeading: "Summary and selected rows", rowItems: ExampleSummarySelectedContent, }, { - testHeading: "Multi-select: emphasized", + testHeading: "Selection mode: multiple, emphasized", rowItems: ExampleMultiSelectContent, }, { - testHeading: "Multi-select: non-emphasized", - isEmphasized: false, + testHeading: "Selection mode: multiple, non-emphasized", rowItems: ExampleMultiSelectContent, + isEmphasized: false, }, { testHeading: "Quiet multi-select: emphasized", @@ -230,7 +238,8 @@ export const TableGroup = Variants({ ], }, { - testHeading: "Collapsible: multi-select", + testHeading: "Selection mode: multiple, collapsible", + selectionMode: "multiple", rowItems: [ { showCheckbox: true, @@ -296,60 +305,100 @@ export const TableGroup = Variants({ ], }, { - testHeading: "Thumbnails", - showThumbnails: true, + testHeading: "End-alignment for numerical data", rowItems: [ { - cellContent: ["Table row alpha", "Test", "2"], + cellContent: ["Pikachu", "Electric", "35"], + textAlignment: { + 2: "end" + } }, { - cellContent: ["Table row bravo", "Test", "28"], + cellContent: ["Charmander", "Fire", "39"], + textAlignment: { + 2: "end" + } }, { - cellContent: [ - "Table row charlie. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", - "Test", - "23", - ], + cellContent: ["Mew", "Psychic", "100"], + textAlignment: { + 2: "end" + } + }, + ], + }, + { + testHeading: "Visual elements", + rowItems: [ + { + cellContent: "Avatar example", + visualElement: "avatar", + }, + { + cellContent: "Icon example", + visualElement: "icon", }, { - cellContent: ["Table row delta", "Test", "7"], + cellContent: "Thumbnail example", + visualElement: "thumbnail", }, ], }, { - testHeading: "Thumbnail: collapsible", - showThumbnails: true, + testHeading: "Visual elements: collapsible", rowItems: [ { cellContent: "Table row alpha", isCollapsible: true, isExpanded: true, tier: 0, - ariaControls: "table-cr-bravo", + ariaControls: "table-cr-bravo table-cr-delta", id: "table-cr-alpha", }, { cellContent: - "Table row bravo. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", + "Table row bravo. There is actually another collapsed row here that's not visible. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", isCollapsible: true, tier: 1, ariaControls: "table-cr-charlie", id: "table-cr-bravo", + visualElement: "avatar", + }, + { + cellContent: [ + "Table row charlie", + "Default not visible", + "Default not visible", + ], + isCollapsible: true, + isHidden: true, + tier: 2, + id: "table-cr-charlie", + }, + { + cellContent: "Selected row delta", + isSelected: true, + isCollapsible: true, + isExpanded: true, + tier: 1, + ariaControls: "table-cr-echo table-cr-foxtrot", + id: "table-cr-delta", + visualElement: "icon", }, { - cellContent: "Table row charlie", + cellContent: "Table row echo", tier: 2, isLastTier: true, isCollapsible: true, - id: "table-cr-charlie", + id: "table-cr-echo", + visualElement: "thumbnail", }, { - cellContent: "Table row delta", + cellContent: "Table row foxtrot", tier: 2, isLastTier: true, isCollapsible: true, - id: "table-cr-delta", + id: "table-cr-foxtrot", }, { cellContent: "Summary row", @@ -370,5 +419,40 @@ export const TableGroup = Variants({ testHeading: "Body drop target", isDropTarget: true, }, + { + testHeading: "Focus, selected states for rows and cells", + rowItems: [ + { + cellContent: "Focused selected row, no rounded corners", + isFocused: true, + isSelected: true, + }, + { + cellContent: "Table row bravo", + }, + { + cellContent: "Selected unfocused row, no rounded corners", + isSelected: true, + }, + { + cellContent: "Focused unselected row, no rounded corners", + isFocused: true, + }, + { + cellContent: ["Table row with a focused cell", "Focused cell", "Unfocused cell"], + cellCustomClasses: { + 1: ["is-focused"] + } + }, + { + cellContent: "Table row echo", + }, + { + cellContent: "Focused selected row, with rounded corners", + isFocused: true, + isSelected: true, + } + ], + } ], }); From 6b7186073b135f68edaa0387d197a50d9ec6e242 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 22 May 2025 14:41:06 -0400 Subject: [PATCH 20/52] chore(table): checkboxes and multi select stories the indeterminate checkbox for a multi-select table should only show in the header row. Single select tables should not have a checkbox in the header row. --- components/table/stories/template.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/components/table/stories/template.js b/components/table/stories/template.js index e5acb53b783..527512dcd4d 100644 --- a/components/table/stories/template.js +++ b/components/table/stories/template.js @@ -299,14 +299,16 @@ export const Template = ({ class="spectrum-Table-headCell spectrum-Table-checkboxCell" role=${ifDefined(useDivs ? "columnheader" : undefined)} > - ${Checkbox({ - size: "m", - isEmphasized: isEmphasized, - isChecked: false, - isIndeterminate: selectionMode === "multiple" ? true : undefined, - customClasses: [`${rootClass}-checkbox`], + ${when(selectionMode === "multiple", () => html` + ${Checkbox({ + size: "m", + isEmphasized: isEmphasized, + isChecked: false, + isIndeterminate: true, + customClasses: [`${rootClass}-checkbox`], }, context)} - ` + `)} + `, )} <${thTag} class="${rootClass}-headCell is-sortable" From 797f16436d903cb038f7f1d1cf1c6de72d1e470b Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 22 May 2025 14:43:08 -0400 Subject: [PATCH 21/52] docs(table): isEmphasized is true by default --- components/table/stories/table.stories.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index 206889f0a28..c8adc6b4a12 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -87,7 +87,7 @@ export default { density: "regular", isQuiet: false, isLoading: false, - isEmphasized: false, + isEmphasized: true, selectionMode: "none", useDivs: false, isDropTarget: false, @@ -270,16 +270,16 @@ SingleSelect.parameters = { }; /** - * Including the `.spectrum-Table--emphasized` class will change the style of selected rows. + * Excluding the `.spectrum-Table--emphasized` class will change the style of selected rows. */ -export const EmphasizedMultiSelect = Template.bind({}); -EmphasizedMultiSelect.storyName = "Selection mode: multiple, emphasized styling"; -EmphasizedMultiSelect.args = { +export const NonEmphasizedMultiSelect = Template.bind({}); +NonEmphasizedMultiSelect.storyName = "Selection mode: multiple, non-emphasized styling"; +NonEmphasizedMultiSelect.args = { ...MultiSelect.args, - isEmphasized: true, + isEmphasized: false, }; -EmphasizedMultiSelect.tags = ["!dev"]; -EmphasizedMultiSelect.parameters = { +NonEmphasizedMultiSelect.tags = ["!dev"]; +NonEmphasizedMultiSelect.parameters = { chromatic: { disableSnapshot: true }, }; From dfef793ee68c34597bada1eab1c7f08bff701004 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Tue, 27 May 2025 17:30:30 -0400 Subject: [PATCH 22/52] docs(table): add specfic alignEnd class for numerical data --- components/table/stories/table.stories.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index c8adc6b4a12..de7d60ba8e3 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -284,7 +284,7 @@ NonEmphasizedMultiSelect.parameters = { }; /** - * Numerical data should generally be end-aligned for the ease of scanning and comparing. Numerical data should only be start-aligned when numbers are arbitrary identifiers, known as “nominal numbers,” which means they can’t be compared or combined arithmetically (e.g., ZIP codes, IP addresses, phone numbers). Column headers follow the alignment of the data. + * Numerical data should generally be end-aligned for the ease of scanning and comparing. Numerical data should only be start-aligned when numbers are arbitrary identifiers, known as “nominal numbers,” which means they can’t be compared or combined arithmetically (e.g., ZIP codes, IP addresses, phone numbers). Column headers follow the alignment of the data, so for end-aligned numerical data, implementations should add the `.spectrum-Table-headCell--alignEnd` class to affected header cells (not shown below). */ export const NumericalData = Template.bind({}); NumericalData.args = { From 4f0a3e222e7d43936a3250c6a9cef8435c351601 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 28 May 2025 09:40:56 -0400 Subject: [PATCH 23/52] revert(table): remove excessive tab index attributes --- components/table/stories/template.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/components/table/stories/template.js b/components/table/stories/template.js index 527512dcd4d..9583aa4815f 100644 --- a/components/table/stories/template.js +++ b/components/table/stories/template.js @@ -113,7 +113,6 @@ export const TableRowItem = ({ aria-selected=${ifDefined(showCheckbox ? "true" : undefined)} data-tier=${ifDefined(tier)} ?hidden=${isHidden} - tabindex="0" > ${when(showCheckbox && !isSectionHeader, () => html` <${cellTag} @@ -124,7 +123,6 @@ export const TableRowItem = ({ [`${rootClass}-cell--alignEnd`]: getTextAlignment(0) === "end", ...cellCustomClasses?.[0]?.reduce((a, c) => ({ ...a, [c]: true }), {}), })} - tabindex="0" > ${when(!isSummaryRow, () => Checkbox({ @@ -149,7 +147,6 @@ export const TableRowItem = ({ [`${rootClass}-cell--alignEnd`]: getTextAlignment(0) === "end", ...cellCustomClasses?.[showCheckbox ? 1 : 0]?.reduce((a, c) => ({ ...a, [c]: true }), {}), })} - tabindex="0" >
${when(!isLastTier, () => @@ -177,7 +174,6 @@ export const TableRowItem = ({ ...cellCustomClasses?.[showCheckbox ? 1 : 0]?.reduce((a, c) => ({ ...a, [c]: true }), {}), })} colspan=${ifDefined(isSectionHeader && showCheckbox ? "4" : isSectionHeader ? "3" : undefined)} - tabindex="0" > ${getCellContent(0)} ` @@ -193,7 +189,6 @@ export const TableRowItem = ({ [`${rootClass}-cell--alignEnd`]: getTextAlignment(1) === "end", ...cellCustomClasses?.[showCheckbox ? 2 : 1]?.reduce((a, c) => ({ ...a, [c]: true }), {}), })} - tabindex="0" >${getCellContent(1)} <${cellTag} @@ -204,7 +199,6 @@ export const TableRowItem = ({ [`${rootClass}-cell--alignEnd`]: getTextAlignment(2) === "end", ...cellCustomClasses?.[showCheckbox ? 3 : 2]?.reduce((a, c) => ({ ...a, [c]: true }), {}), })} - tabindex="0" >${getCellContent(2)}` )} From 9f48d07333b070159e890056f321c70fdea0e420 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 28 May 2025 11:33:10 -0400 Subject: [PATCH 24/52] docs(table): update docs with tab stops info --- components/table/stories/table.stories.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index de7d60ba8e3..0f4104bfaa8 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -315,7 +315,7 @@ NumericalData.parameters = { }; /** - * The cells and rows within the table have different states based on selection and focus. + * The cells and rows within the table have different states based on selection and focus. Implementations should add appropriate tab stops to indicate focus on a row correctly. */ export const TableStates = Template.bind({}); TableStates.args = { From 97319269976523774a8e2c0ec05c4967ebf9913f Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 28 May 2025 13:37:15 -0400 Subject: [PATCH 25/52] docs(table): add sortable column story - adds isSortable and sortIcon args - implements new args into template - refactors the head cells to correctly use aria-sort (i.e. only one column supports sorting instead of all 3) --- components/table/stories/table.stories.js | 43 +++++++++++++++++++++++ components/table/stories/template.js | 43 +++++++++-------------- 2 files changed, 59 insertions(+), 27 deletions(-) diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index 0f4104bfaa8..aa6dca91778 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -68,6 +68,28 @@ export default { }, control: "boolean", }, + isSortable: { + name: "Enable sortable columns", + description: "Table columns can be sortable and are indicated by appropriate sort icons.", + type: { name: "boolean" }, + table: { + type: { summary: "boolean" }, + category: "Component", + }, + control: "boolean", + }, + sortIcon: { + name: "Sort icon", + description: "Sort icons are used to indicate the sort direction of a column. The `none` option is used to indicate that the column is not sorted.", + type: { name: "string" }, + table: { + type: { summary: "string" }, + category: "Component", + }, + options: ["Sort", "SortUp", "SortDown"], + control: "select", + if: { arg: "isSortable", eq: true }, + }, isDropTarget: { name: "Dropzone (drop target)", type: { name: "boolean" }, @@ -93,6 +115,8 @@ export default { isDropTarget: false, useScroller: false, hasColumnDividers: false, + isSortable: false, + sortIcon: "Sort", rowItems: [ { cellContent: "Row item alpha", @@ -370,6 +394,25 @@ Quiet.parameters = { chromatic: { disableSnapshot: true }, }; +/** + * Tables can enable column sorting. The `aria-sort` attribute should be set on the `.spectrum-Table-headCell` element for the column that is currently sorted. `aria-sort` should not be set on more than one column at a time. + * + * When a column is sorted in ascending order, use the `SortUp` icon, as seen below. If sorted in descending order, use the `SortDown` icon. If the sort is undefined, use the more general `Sort` icon. + * + * Implementations will develop their own sorting functionality. Additionally, the `aria-sort` attribute should be removed from a column header when the column is not sorted. + */ +export const Sortable = Template.bind({}); +Sortable.args = { + ...Default.args, + isSortable: true, + sortIcon: "SortUp", +}; +Sortable.parameters = { + chromatic: { disableSnapshot: true }, +}; +Sortable.storyName = "Sortable columns"; +Sortable.tags = ["!dev"]; + /** * A quiet multi-select table has emphasized styling by default, but excluding the `.spectrum-Table--emphasized` class will change the style of selected rows. */ diff --git a/components/table/stories/template.js b/components/table/stories/template.js index 9583aa4815f..01baa30ecc4 100644 --- a/components/table/stories/template.js +++ b/components/table/stories/template.js @@ -217,6 +217,8 @@ export const Template = ({ visualElement, isDropTarget = false, hasColumnDividers = false, + isSortable = false, + sortIcon = "Sort", rowItems = [], customClasses = [], id = getRandomId("table"), @@ -268,6 +270,7 @@ export const Template = ({ }; const useCheckboxCell = rowItems.some((item) => item.showCheckbox === true); + const ariaSortValue = sortIcon === "SortUp" ? "ascending" : sortIcon === "SortDown" ? "descending" : "none"; const tableHtml = html` <${tableTag} @@ -305,47 +308,33 @@ export const Template = ({ `, )} <${thTag} - class="${rootClass}-headCell is-sortable" + class=${classMap({ + [`${rootClass}-headCell`]: true, + ["is-sortable"]: isSortable, + ["is-sorted-asc"]: sortIcon === "SortUp", + ["is-sorted-desc"]: sortIcon === "SortDown", + })} role=${ifDefined(useDivs ? "columnheader" : undefined)} - aria-sort="none" - tabindex="0" + aria-sort=${ifDefined(isSortable ? ariaSortValue : undefined)} + tabindex=${ifDefined(isSortable ? "0" : undefined)} > - ${Icon({ - iconName: "Sort", + ${when(isSortable, () => Icon({ + iconName: sortIcon, setName: "workflow", customClasses: [`${rootClass}-sortIcon`], - }, context)} + }, context))} Column title <${thTag} - class="${rootClass}-headCell is-sortable is-sorted-desc" + class="${rootClass}-headCell" role=${ifDefined(useDivs ? "columnheader" : undefined)} - aria-sort="descending" - tabindex="0" > - ${Icon({ - iconName: "SortDown", - setName: "workflow", - customClasses: [`${rootClass}-sortIcon`], - }, context)} Column title - ${Icon({ - iconName: "ChevronDown100", - setName: "ui", - customClasses: [`${rootClass}-menuIcon`], - }, context)} <${thTag} - class="${rootClass}-headCell is-sortable is-sorted-asc" + class="${rootClass}-headCell" role=${ifDefined(useDivs ? "columnheader" : undefined)} - aria-sort="ascending" - tabindex="0" > - ${Icon({ - iconName: "SortUp", - setName: "workflow", - customClasses: [`${rootClass}-sortIcon`], - }, context)} Column title From e5f27bc52ddac1af04639ce519f31af78ae097f6 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 28 May 2025 13:39:30 -0400 Subject: [PATCH 26/52] docs(table): adds docs for loading, cleans up numerical docs --- components/table/stories/table.stories.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index aa6dca91778..7019d9f6ec9 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -204,6 +204,9 @@ EmptyState.parameters = { }; EmptyState.storyName = "Empty state"; +/** + * A table may render a [progress circle](/docs/components-progress-circle--docs) while populating data. + */ export const LoadingState = Template.bind({}); LoadingState.args = { ...Default.args, @@ -214,6 +217,7 @@ LoadingState.parameters = { chromatic: { disableSnapshot: true }, }; LoadingState.storyName = "Loading state"; + /** * The compact variant decreases the spacing used within the table rows, except for the header row. */ @@ -308,7 +312,9 @@ NonEmphasizedMultiSelect.parameters = { }; /** - * Numerical data should generally be end-aligned for the ease of scanning and comparing. Numerical data should only be start-aligned when numbers are arbitrary identifiers, known as “nominal numbers,” which means they can’t be compared or combined arithmetically (e.g., ZIP codes, IP addresses, phone numbers). Column headers follow the alignment of the data, so for end-aligned numerical data, implementations should add the `.spectrum-Table-headCell--alignEnd` class to affected header cells (not shown below). + * Numerical data should generally be end-aligned for the ease of scanning and comparing. Numerical data should only be start-aligned when numbers are arbitrary identifiers, known as “nominal numbers,” which means they can’t be compared or combined arithmetically (e.g., ZIP codes, IP addresses, phone numbers). + * + * Column headers follow the alignment of the data, so for end-aligned numerical data, implementations should add the `.spectrum-Table-headCell--alignEnd` class to affected header cells (not shown below). */ export const NumericalData = Template.bind({}); NumericalData.args = { From e14ed8411e48404384c5c57824d83aaae8ed2e47 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 28 May 2025 14:02:15 -0400 Subject: [PATCH 27/52] refactor(table): body drop target indicator is ::after - to avoid the outline being covered by the border, we now use an ::after pseudo-element laid on top of the body borders - fixes linting issues with table-header-row-top-to-text/bottom-to-text --- components/table/index.css | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/components/table/index.css b/components/table/index.css index dfc530a7d89..0946a152a95 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -357,13 +357,24 @@ display: table-row-group; &.is-drop-target { - /* Make sure negative offset outline is not covered by borders. */ + /* Make sure borders are not visible behind the pseudo-element */ --mod-table-border-color: transparent; - outline-width: var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness)); - outline-style: solid; - outline-color: var(--highcontrast-table-focus-indicator-color, var(--mod-table-drop-zone-outline-color, var(--spectrum-table-drop-zone-outline-color))); - outline-offset: calc(-1 * var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness))); + /* Remove the outline since we'll replace it with an ::after pseudo-element */ + outline: none; + position: relative; + z-index: 0; + + &::after { + content: ""; + position: absolute; + inset: 0; + border-end-start-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); + border-end-end-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); + border: var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness)) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-drop-zone-outline-color, var(--spectrum-table-drop-zone-outline-color))); + pointer-events: none; + z-index: 1; + } } } @@ -632,7 +643,7 @@ /* Make sure shift-tab reverse keyboard navigation keeps the whole cell in focus. --mod-table-current-header-height should be dynamically updated with JS to match the table header height. */ - scroll-padding-top: var(--mod-table-current-header-height, calc((var(--mod-table-header-line-height, var(--spectrum-table-row-line-height)) * var(--mod-table-header-font-size, var(--spectrum-table-row-font-size))) + var(--mod-table-header-top-to-text, var(--spectrum-table-header-top-to-text)) + var(--mod-table-header-bottom-to-text, var(--spectrum-table-header-bottom-to-text)) + var(--mod-table-border-width, var(--spectrum-table-border-width)))); + scroll-padding-top: var(--mod-table-current-header-height, calc((var(--mod-table-header-line-height, var(--spectrum-table-row-line-height)) * var(--mod-table-header-font-size, var(--spectrum-table-row-font-size))) + var(--mod-table-header-row-top-to-text, var(--spectrum-table-header-row-top-to-text)) + var(--mod-table-header-row-bottom-to-text, var(--spectrum-table-header-row-bottom-to-text)) + var(--mod-table-border-width, var(--spectrum-table-border-width)))); &.spectrum-Table--quiet { border-block-start: none; From 90cbf3ea89484e4a1a049e4f637b618586bb455f Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 28 May 2025 14:09:28 -0400 Subject: [PATCH 28/52] fix(table): fix layout shift when row is focused --- components/table/index.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/table/index.css b/components/table/index.css index 0946a152a95..7b11b71352c 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -505,6 +505,9 @@ border-inline-start: 0; border-block: var(--spectrum-table-row-focus-indicator-outline-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); + /* Because the ::before element technically removes the table's inline border, the first cell needs the entire cell padding to avoid horizontal layout shift when a row is focused. */ + padding-inline-start: var(--spectrum-table-cell-inline-spacing); + /* The side row focus indicator line */ &::before { content: ""; From eccd93702236b20e6192daeefd4e2575ad9445e6 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 28 May 2025 15:46:25 -0400 Subject: [PATCH 29/52] chore(table): update metadata --- components/table/dist/metadata.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/components/table/dist/metadata.json b/components/table/dist/metadata.json index bf4a2130177..6307ad20710 100644 --- a/components/table/dist/metadata.json +++ b/components/table/dist/metadata.json @@ -15,6 +15,7 @@ ".spectrum-Table-body.is-drop-target", ".spectrum-Table-body.is-drop-target .spectrum-Table-row", ".spectrum-Table-body.is-drop-target .spectrum-Table-row .spectrum-Table-checkbox .spectrum-Checkbox-box:before", + ".spectrum-Table-body.is-drop-target:after", ".spectrum-Table-cell", ".spectrum-Table-cell--alignEnd", ".spectrum-Table-cell--alignStart", @@ -167,7 +168,6 @@ "--mod-table-header-background-color", "--mod-table-header-background-color-quiet", "--mod-table-header-background-color-scrollable", - "--mod-table-header-bottom-to-text", "--mod-table-header-checkbox-block-spacing", "--mod-table-header-font-family", "--mod-table-header-font-size", @@ -178,7 +178,6 @@ "--mod-table-header-row-top-to-text", "--mod-table-header-text-color", "--mod-table-header-text-transform", - "--mod-table-header-top-to-text", "--mod-table-header-vertical-align", "--mod-table-icon-color-active", "--mod-table-icon-color-default", @@ -262,14 +261,12 @@ "--spectrum-table-focus-indicator-color", "--spectrum-table-focus-indicator-thickness", "--spectrum-table-header-background-color", - "--spectrum-table-header-bottom-to-text", "--spectrum-table-header-font-weight", "--spectrum-table-header-row-bottom-to-text", "--spectrum-table-header-row-checkbox-block-spacing", "--spectrum-table-header-row-checkbox-to-top-medium", "--spectrum-table-header-row-top-to-text", "--spectrum-table-header-text-color", - "--spectrum-table-header-top-to-text", "--spectrum-table-icon-color", "--spectrum-table-icon-color-active", "--spectrum-table-icon-color-default", From ae0a85a1a1bf553dc08c0954d33f6afae4c1f202 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 28 May 2025 17:12:50 -0400 Subject: [PATCH 30/52] docs(table): add sort test case --- components/table/stories/table.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/table/stories/table.test.js b/components/table/stories/table.test.js index a65ad40ed4b..9c38409442e 100644 --- a/components/table/stories/table.test.js +++ b/components/table/stories/table.test.js @@ -169,6 +169,10 @@ export const TableGroup = Variants({ useScroller: true, rowItems: ExampleMultiSelectContent, }, + { + testHeading: "Sortable columns: Sort", + isSortable: true, + }, { testHeading: "Section headers", rowItems: ExampleSectionHeadersContent, From 1f4827577b3dcf27137d36e0a683ca17aa829181 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 28 May 2025 17:44:37 -0400 Subject: [PATCH 31/52] chore(table): add changeset --- .changeset/petite-toys-greet.md | 87 +++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 .changeset/petite-toys-greet.md diff --git a/.changeset/petite-toys-greet.md b/.changeset/petite-toys-greet.md new file mode 100644 index 00000000000..0a06b9bb50b --- /dev/null +++ b/.changeset/petite-toys-greet.md @@ -0,0 +1,87 @@ +--- +"@spectrum-css/table": major +--- + +### S2 table migration + +Compared to the S1 table, this component has updated corner rounding, updated color tokens, some updated spacing, and an overall refreshed look. + +#### Net-new features + +- The S2 table supports an empty state, rendering an illustrated message component. +- As data is loading to the table, this component will render a progress circle during the loading state. +- There are 2 selection modes: single-select and multi-select. Multi-select tables (`selectionMode: "multiple"`) render an indeterminate checkbox in the `thead`/`div` equivalent. Single-select tables (`selectionMode: "single"`) do not render the indeterminate checkbox in the header row. +- For tables with sortable column, there are three new S2 icons used: `Sort` to indicate "general" sorting, `SortUp` for ascending sort direction, `SortDown` for descending sort direction. +- Tables support thumbnail, avatar, and icon components as content within a cell. +- Focus indicators for entire rows have been updated for rows to include a side focus indicator. + +#### Description of other S2 table work + +T-shirt sizing for tables is not technically supported, so t-shirt size classes (i.e. `.spectrum-Table--sizeS`), has been removed across all density variants. + +The `.spectrum-Table-cell--alignRight` class has been renamed to `.spectrum-Table-cell--alignEnd` to reflect the preference for "logical" positioning. The `.spectrum-Table-cell--alignCenter` class has been refactored to `.spectrum-Table-cell--alignStart`. + +In S1, the CSS table component only supported thumbnails. Because the S2 table supports thumbnails, as well as avatars and icons, most of the language regarding thumbnails has also changed. For instance, the `showThumbnail` storybook arg was refactored to `visualElement`. The following CSS classes have been removed or refactored to remove the thumbnail language in favor of "visual" instead: + +- `.spectrum-Table-thumbnailInner` >> `.spectrum-Table-visualInner` + +- `.spectrum-Table-row--thumbnail` >> **removed** +- `.spectrum-Table-cell--thumbnail` >> **removed** +- `.spectrum-Table-thumbnailInner` >> **removed** +- `.spectrum-Table-thumbnailContent` >> **removed** +- `.spectrum-Table-thumbnailInner` >> **removed** + +The drop zones have been refactored to approach the drop zone indicators as pseudo elements, as opposed to `outline` properties. + +Because there are multiple sort icons in the S2 table, `.spectrum-Table-sortedIcon` has been renamed to `.spectrum-Table-sortIcon`. + +Individual cell focus rings have rounded corners. + +The outer table border extends all the way around the `thead`/`div` equivalent. + +In Storybook, several new stories have been added to the docs page and the testing grid for Chromatic. These stories include `EmptyState`, `LoadingState`, `SingleSelect`, `NumericalData`, `TableStates`, `Sortable`. Documentation has been added for each of these stories, as well as expanded in other stories. + +#### Mods + +##### Renamed Modifiers + +| Old Modifier | New modifier | +| -------------------------------------------------- | ------------------------------------------------- | +| `--mod-table-border-radius--quiet` | `--mod-table-border-radius-quiet` | +| `--mod-table-header-top-to-text` | `--mod-table-header-row-top-to-text` | +| `--mod-table-header-bottom-to-text` | `--mod-table-header-row-bottom-to-text` | +| `--mod-table-cell-inline-space` | `--mod-table-cell-inline-spacing` | +| `--mod-table-checkbox-to-text` | `--mod-table-checkbox-to-cell-content` | +| `--mod-table-header-background-color--quiet` | `--mod-table-header-background-color-quiet` | +| `--mod-table-header-bottom-to-text` | `--mod-table-header-row-bottom-to-text` | +| `--mod-table-header-top-to-text` | `--mod-table-header-row-top-to-text` | +| `--mod-table-min-row-height--compact` | `--mod-table-min-row-height-compact` | +| `--mod-table-min-row-height--spacious` | `--mod-table-min-row-height-spacious` | +| `--mod-table-outer-border-inline-width--quiet` | `--mod-table-outer-border-inline-width-quiet` | +| `--mod-table-row-background-color--quiet` | `--mod-table-row-background-color-quiet` | +| `--mod-table-row-checkbox-block-spacing--compact` | `--mod-table-row-checkbox-block-spacing-compact` | +| `--mod-table-row-checkbox-block-spacing--spacious` | `--mod-table-row-checkbox-block-spacing-spacious` | +| `--mod-table-thumbnail-to-text` | `--mod-table-visual-to-text` | + +##### New Modifiers + +- `--mod-table-avatar-size` +- `--mod-table-avatar-size-compact` +- `--mod-table-avatar-size-spacious` +- `--mod-table-section-header-inline-start-spacing` +- `--mod-table-summary-row-bottom-to-text` +- `--mod-table-summary-row-min-height` +- `--mod-table-summary-row-top-to-text` + +##### Removed Modifiers + +- `--mod-table-edge-to-content` +- `--mod-table-header-row-checkbox-block-spacing` +- `--mod-table-avatar-size-spacious` +- `--mod-table-row-bottom-to-text--compact` +- `--mod-table-row-bottom-to-text--spacious` +- `--mod-table-row-top-to-text--compact` +- `--mod-table-row-top-to-text--spacious` +- `--mod-table-thumbnail-block-spacing"` +- `--mod-table-thumbnail-block-spacing-compact` +- `--mod-table-thumbnail-block-spacing-spacious` From 28d197c0a1b792f1527ceea4030c6698e5ef7c57 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 29 May 2025 13:05:20 -0400 Subject: [PATCH 32/52] fix(table): remove border-block-end from header row -instead, continue the use of the individual cell border-block-start in the quiet variant, but use the divider color instead of border-color --- components/table/index.css | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/components/table/index.css b/components/table/index.css index 7b11b71352c..48209b5d222 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -306,13 +306,6 @@ } } -/* The quiet table's header row cells need a border-block-end to match the body cells. */ -.spectrum-Table--quiet { - .spectrum-Table-headCell { - border-block-end: var(--mod-table-border-width, var(--spectrum-table-border-width)) solid var(--highcontrast-table-divider-color, var(--mod-table-divider-color, var(--spectrum-table-divider-color))); - } -} - .spectrum-Table-sortIcon { &:last-child .spectrum-Table-headCell { border-start-end-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); @@ -438,7 +431,7 @@ /* Outside border (the table's border) */ .spectrum-Table-body .spectrum-Table-row { &:first-child .spectrum-Table-cell { - border-block-start: var(--mod-table-border-width, var(--spectrum-table-border-width)) solid var(--highcontrast-table-border-color, var(--mod-table-border-color, var(--spectrum-table-border-color))); + border-block-start: var(--mod-table-border-width, var(--spectrum-table-border-width)) solid var(--highcontrast-table-border-color, var(--mod-table-divider-color, var(--spectrum-table-divider-color))); } &:last-child .spectrum-Table-cell { From 85ca47996e282a7a2d64f31f2ddc671e973d4786 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 29 May 2025 13:16:12 -0400 Subject: [PATCH 33/52] fix(table): update border-radius for focused cells - updates a missing token for focused cells - removes unused token for row focus indicator outline width --- components/table/index.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/table/index.css b/components/table/index.css index 48209b5d222..b01470cbb27 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -49,7 +49,9 @@ --spectrum-table-focus-indicator-thickness: var(--spectrum-focus-indicator-thickness); --spectrum-table-focus-indicator-color: var(--spectrum-focus-indicator-color); --spectrum-table-row-focus-indicator-width: var(--spectrum-side-focus-indicator); - --spectrum-table-row-focus-indicator-outline-width: 1px; + + /* Individual focused table cells still have a border-radius */ + --spectrum-table-focused-cell-border-radius: var(--spectrum-corner-radius-small-default); --spectrum-table-selected-cell-background-color-focus: var(--highcontrast-table-selected-row-background-color-focus, var(--mod-table-selected-row-background-color-non-emphasized-focus, var(--spectrum-table-selected-row-background-color-non-emphasized-focus))); @@ -415,7 +417,7 @@ content: ""; position: absolute; inset: calc(-1 * var(--mod-table-border-width, var(--spectrum-table-border-width))); - border-radius: calc(var(--mod-table-border-radius, var(--spectrum-table-border-radius)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); + border-radius: calc(var(--mod-table-border-radius, var(--spectrum-table-focused-cell-border-radius)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); border: var(--mod-table-focus-indicator-thickness, var(--spectrum-table-focus-indicator-thickness)) solid var(--highcontrast-table-cell-focus-indicator-color, var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color)))); pointer-events: none; } From d44d1fb451defe2ea6bc087747e1040771e7e987 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 29 May 2025 14:25:37 -0400 Subject: [PATCH 34/52] fix(table): remove old custom property ref to please linter --- components/table/index.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/table/index.css b/components/table/index.css index b01470cbb27..bc566f23f26 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -498,7 +498,7 @@ .spectrum-Table-cell:first-child { position: relative; border-inline-start: 0; - border-block: var(--spectrum-table-row-focus-indicator-outline-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); + border-block: var(--spectrum-table-border-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); /* Because the ::before element technically removes the table's inline border, the first cell needs the entire cell padding to avoid horizontal layout shift when a row is focused. */ padding-inline-start: var(--spectrum-table-cell-inline-spacing); @@ -520,13 +520,13 @@ &:first-child .spectrum-Table-cell, &:last-child .spectrum-Table-cell, .spectrum-Table-cell { - border-block: var(--spectrum-table-row-focus-indicator-outline-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); + border-block: var(--spectrum-table-border-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); } /* Last cell of focused row needs the fancy new focus indicator borders at the end */ .spectrum-Table-cell:last-child { - border-inline-end: var(--spectrum-table-row-focus-indicator-outline-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); - border-block-end: var(--spectrum-table-row-focus-indicator-outline-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); + border-inline-end: var(--spectrum-table-border-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); + border-block-end: var(--spectrum-table-border-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); } /* In a focused row that is last in the table, the first cell needs rounded corners at the bottom of the row focus indicator line */ From 04ae4c2f20eb00bbde67a4b104d938975e2e8dbc Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 29 May 2025 14:27:20 -0400 Subject: [PATCH 35/52] chore(table): update metadata --- components/table/dist/metadata.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/table/dist/metadata.json b/components/table/dist/metadata.json index 6307ad20710..33866e8f02f 100644 --- a/components/table/dist/metadata.json +++ b/components/table/dist/metadata.json @@ -5,7 +5,6 @@ ".spectrum-Table--compact", ".spectrum-Table--emphasized", ".spectrum-Table--quiet", - ".spectrum-Table--quiet .spectrum-Table-headCell", ".spectrum-Table--spacious", ".spectrum-Table-body", ".spectrum-Table-body .spectrum-Table-row .spectrum-Table-cell:first-child", @@ -260,6 +259,7 @@ "--spectrum-table-edge-to-content", "--spectrum-table-focus-indicator-color", "--spectrum-table-focus-indicator-thickness", + "--spectrum-table-focused-cell-border-radius", "--spectrum-table-header-background-color", "--spectrum-table-header-font-weight", "--spectrum-table-header-row-bottom-to-text", @@ -289,7 +289,6 @@ "--spectrum-table-row-checkbox-to-top-medium-compact", "--spectrum-table-row-checkbox-to-top-medium-spacious", "--spectrum-table-row-down-opacity", - "--spectrum-table-row-focus-indicator-outline-width", "--spectrum-table-row-focus-indicator-width", "--spectrum-table-row-font-family", "--spectrum-table-row-font-size", @@ -343,6 +342,7 @@ "--spectrum-component-height-100", "--spectrum-component-top-to-text-100", "--spectrum-corner-radius-medium-size-extra-small", + "--spectrum-corner-radius-small-default", "--spectrum-default-font-style", "--spectrum-drop-zone-background-color-opacity", "--spectrum-drop-zone-background-color-rgb", From 1c2ba8539192cebe850bdc4b8e8f54f5cb224d84 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Mon, 2 Jun 2025 13:55:33 -0400 Subject: [PATCH 36/52] docs(table): add story for action button menu - adds story for a table with a menu action button in the header cell - adds hasMenu story arg and new markup --- components/table/stories/table.stories.js | 24 +++++++++++++++++++++++ components/table/stories/template.js | 18 +++++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index 7019d9f6ec9..41619d5e250 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -68,6 +68,16 @@ export default { }, control: "boolean", }, + hasMenu: { + name: "Has a menu", + description: "Adds an action button to header cell to indicate that the column has a hidden menu.", + type: { name: "boolean" }, + table: { + type: { summary: "boolean" }, + category: "Component", + }, + control: "boolean", + }, isSortable: { name: "Enable sortable columns", description: "Table columns can be sortable and are indicated by appropriate sort icons.", @@ -419,6 +429,20 @@ Sortable.parameters = { Sortable.storyName = "Sortable columns"; Sortable.tags = ["!dev"]; +/** + * Tables can have additional actions in the header cells that could trigger a menu. The content of the header cell is an action button](/docs/components-action-button--docs). + */ +export const WithMenuButton = Template.bind({}); +WithMenuButton.args = { + ...Default.args, + hasMenu: true, +}; +WithMenuButton.storyName = "With action button"; +WithMenuButton.parameters = { + chromatic: { disableSnapshot: true }, +}; +WithMenuButton.tags = ["!dev"]; + /** * A quiet multi-select table has emphasized styling by default, but excluding the `.spectrum-Table--emphasized` class will change the style of selected rows. */ diff --git a/components/table/stories/template.js b/components/table/stories/template.js index 01baa30ecc4..c80a7a9dacf 100644 --- a/components/table/stories/template.js +++ b/components/table/stories/template.js @@ -1,3 +1,4 @@ +import { Template as ActionButton } from "@spectrum-css/actionbutton/stories/template.js"; import { Template as Avatar } from "@spectrum-css/avatar/stories/template.js"; import { Template as Button } from "@spectrum-css/button/stories/template.js"; import { Template as Checkbox } from "@spectrum-css/checkbox/stories/template.js"; @@ -219,6 +220,7 @@ export const Template = ({ hasColumnDividers = false, isSortable = false, sortIcon = "Sort", + hasMenu = false, rowItems = [], customClasses = [], id = getRandomId("table"), @@ -326,10 +328,21 @@ export const Template = ({ Column title <${thTag} - class="${rootClass}-headCell" + class="${rootClass}-headCell ${hasMenu ? `${rootClass}-menuButton` : ""}" role=${ifDefined(useDivs ? "columnheader" : undefined)} > - Column title + ${when(hasMenu, () => ActionButton({ + size: "m", + isQuiet: true, + iconName: "ChevronDown100", + iconSet: "ui", + iconOnly: true, + label: "Column title", + customClasses: [`${rootClass}-menuButton`], + }, context), + () => html` + Column title + `)} <${thTag} class="${rootClass}-headCell" @@ -353,6 +366,7 @@ export const Template = ({ useDivs, visualElement, hasColumnDividers, + hasMenu, isEmphasized, ...item, }, context) From 75a70c7e11f32fef54ea8a0ae08e61cf32622166 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Mon, 2 Jun 2025 14:02:23 -0400 Subject: [PATCH 37/52] feat(table): add menu button styles --- components/table/index.css | 59 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/components/table/index.css b/components/table/index.css index bc566f23f26..c641af81875 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -114,6 +114,21 @@ --mod-avatar-block-size: var(--mod-table-avatar-size, var(--spectrum-table-avatar-size)); --mod-avatar-inline-size: var(--mod-table-avatar-size, var(--spectrum-table-avatar-size)); + /* Action button for menus in header cells */ + --mod-action-button-icon-size: var(--spectrum-table-disclosure-icon-size); + --mod-actionbutton-font-weight: var(--spectrum-table-header-font-weight); + --mod-actionbutton-font-size: var(--spectrum-table-row-font-size); + --mod-actionbutton-font-family: var(--spectrum-table-row-font-family); + --mod-actionbutton-line-height: var(--spectrum-table-row-line-height); + --mod-actionbutton-background-color-hover: transparent; + --mod-actionbutton-background-color-focus: transparent; + --mod-actionbutton-background-color-down: transparent; + --mod-actionbutton-content-color-default: var(--spectrum-table-header-text-color); + --mod-actionbutton-edge-to-text: var(--spectrum-table-cell-inline-spacing); + --mod-actionbutton-text-to-visual: var(--spectrum-table-visual-to-text); + --mod-actionbutton-edge-to-visual: var(--spectrum-table-cell-inline-spacing); + --mod-actionbutton-focus-indicator-border-radius: var(--spectrum-table-focused-cell-border-radius); + &:dir(rtl) { --spectrum-logical-rotation: matrix(-1, 0, 0, 1, 0, 0); } @@ -222,6 +237,32 @@ --spectrum-table-icon-color: var(--highcontrast-table-icon-color-focus, var(--mod-table-icon-color-key-focus, var(--spectrum-table-icon-color-key-focus))); } } + + /* These styles get applied to the headCell when the headCell has the --menuButton modifier class. */ + &.spectrum-Table-menuButton { + --spectrum-table-cell-inline-spacing: 0; + --spectrum-table-header-row-top-to-text: 0; + --spectrum-table-header-row-bottom-to-text: 0; + } + + /* These styles get applied to the icon within that menu button. */ + & .spectrum-Table-menuButton { + --mod-icon-color: var(--spectrum-table-icon-color); + + &:hover { + --mod-icon-color: var(--spectrum-table-icon-color-hover); + } + + &:active { + --mod-icon-color: var(--spectrum-table-icon-color-active); + } + + &:focus, + &:focus:hover, + &:focus-visible { + --mod-icon-color: var(--spectrum-table-icon-color-key-focus); + } + } } .spectrum-Table-scroller { @@ -281,6 +322,11 @@ } } + /* Places the focus indicator above the table cell borders. */ + .spectrum-ActionButton::after { + z-index: 1; + } + &:first-child .spectrum-Table-headCell:first-child { border-start-start-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); border-inline-start: var(--mod-table-border-width, var(--spectrum-table-border-width)) solid var(--highcontrast-table-border-color, var(--mod-table-border-color, var(--spectrum-table-border-color))); @@ -317,6 +363,19 @@ /* Head cell column text */ .spectrum-Table-columnTitle { display: inline-block; + vertical-align: middle; +} + +/* Head cell column text in the action button */ +.spectrum-Table-menuButton { + .spectrum-ActionButton { + flex-direction: row-reverse; + min-block-size: var(--spectrum-table-min-header-row-height); + + /* Allows the action button to wrap to the next line if the text + icon size is too long. */ + min-inline-size: unset; + flex-wrap: wrap-reverse; + } } /********* ICONS- SORT, DISCLOSURE/MENU *********/ From af9a99f9773760b47bee051fdbdfd0dd23fe14ad Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Tue, 3 Jun 2025 10:41:13 -0400 Subject: [PATCH 38/52] chore(table): update metadata --- components/table/dist/metadata.json | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/components/table/dist/metadata.json b/components/table/dist/metadata.json index 33866e8f02f..2fd6a4105b4 100644 --- a/components/table/dist/metadata.json +++ b/components/table/dist/metadata.json @@ -41,6 +41,12 @@ ".spectrum-Table-head", ".spectrum-Table-head [role=\"row\"]", ".spectrum-Table-headCell", + ".spectrum-Table-headCell .spectrum-Table-menuButton", + ".spectrum-Table-headCell .spectrum-Table-menuButton:active", + ".spectrum-Table-headCell .spectrum-Table-menuButton:focus", + ".spectrum-Table-headCell .spectrum-Table-menuButton:focus-visible", + ".spectrum-Table-headCell .spectrum-Table-menuButton:focus:hover", + ".spectrum-Table-headCell .spectrum-Table-menuButton:hover", ".spectrum-Table-headCell.is-focused", ".spectrum-Table-headCell.is-focused:after", ".spectrum-Table-headCell.is-sortable.is-keyboardFocused", @@ -49,8 +55,10 @@ ".spectrum-Table-headCell.is-sortable:focus-visible", ".spectrum-Table-headCell.is-sortable:focus:hover", ".spectrum-Table-headCell.is-sortable:hover", + ".spectrum-Table-headCell.spectrum-Table-menuButton", ".spectrum-Table-headCell:focus-visible", ".spectrum-Table-headCell:focus-visible:after", + ".spectrum-Table-headRow .spectrum-ActionButton:after", ".spectrum-Table-headRow .spectrum-Table-checkboxCell", ".spectrum-Table-headRow .spectrum-Table-checkboxCell .spectrum-Table-checkbox", ".spectrum-Table-headRow .spectrum-Table-headCell", @@ -58,6 +66,7 @@ ".spectrum-Table-headRow:first-child .spectrum-Table-headCell:first-child", ".spectrum-Table-headRow:last-child .spectrum-Table-headCell:last-child", ".spectrum-Table-main", + ".spectrum-Table-menuButton .spectrum-ActionButton", ".spectrum-Table-menuIcon", ".spectrum-Table-row", ".spectrum-Table-row .is-emphasized", @@ -375,9 +384,23 @@ "--spectrum-transparent-white-25" ], "passthroughs": [ + "--mod-action-button-icon-size", + "--mod-actionbutton-background-color-down", + "--mod-actionbutton-background-color-focus", + "--mod-actionbutton-background-color-hover", + "--mod-actionbutton-content-color-default", + "--mod-actionbutton-edge-to-text", + "--mod-actionbutton-edge-to-visual", + "--mod-actionbutton-focus-indicator-border-radius", + "--mod-actionbutton-font-family", + "--mod-actionbutton-font-size", + "--mod-actionbutton-font-weight", + "--mod-actionbutton-line-height", + "--mod-actionbutton-text-to-visual", "--mod-avatar-block-size", "--mod-avatar-inline-size", "--mod-checkbox-margin-block", + "--mod-icon-color", "--mod-thumbnail-size" ], "high-contrast": [ From 6d3c43954f65805d7c3254419fc0fc6b011ba0b3 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Tue, 3 Jun 2025 11:14:27 -0400 Subject: [PATCH 39/52] fix(table): add mod back in --- components/table/index.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/table/index.css b/components/table/index.css index c641af81875..ff59fe25b46 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -560,7 +560,7 @@ border-block: var(--spectrum-table-border-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); /* Because the ::before element technically removes the table's inline border, the first cell needs the entire cell padding to avoid horizontal layout shift when a row is focused. */ - padding-inline-start: var(--spectrum-table-cell-inline-spacing); + padding-inline-start: var(--mod-table-cell-inline-spacing,var(--spectrum-table-cell-inline-spacing)); /* The side row focus indicator line */ &::before { From fe2d4d65804aa570ff566f7bb2a5fc98cb81c6e7 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Tue, 3 Jun 2025 12:05:36 -0400 Subject: [PATCH 40/52] docs(table): update table stories file - sets the hasMenu arg to false - adds a better description for the emphasized arg - adds argTypes to the EmptyState story - fixes the action button link - changes "left-aligned" to "start-aligned" - removes section header quiet from side nav --- components/table/stories/table.stories.js | 27 ++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index 41619d5e250..7c5f1671696 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -22,7 +22,10 @@ export default { control: "select", }, isQuiet, - isEmphasized, + isEmphasized: { + ...isEmphasized, + description: "Emphasized styling on the table affects colors of selected rows and any checkboxes." + }, isLoading, useDivs: { name: "Use divs for markup", @@ -125,6 +128,7 @@ export default { isDropTarget: false, useScroller: false, hasColumnDividers: false, + hasMenu: false, isSortable: false, sortIcon: "Sort", rowItems: [ @@ -194,7 +198,7 @@ const ExampleRowItems = [ ]; /** - * The default table also uses the regular density. Similar to a paragraph of text, textual data is always left-aligned within a table. Never use center alignment. + * The default table also uses the regular density. Similar to a paragraph of text, textual data is always start-aligned within a table. Never use center alignment. * * Tables with sortable columns can show different states of sorting: unsorted, ascending, and descending. Additionally, tables can also trigger a menu, as indicated by the chevron. */ @@ -213,6 +217,18 @@ EmptyState.parameters = { chromatic: { disableSnapshot: true }, }; EmptyState.storyName = "Empty state"; +EmptyState.argTypes = { + isEmphasized: { table: { disable: true } }, + isQuiet: { table: { disable: true } }, + isLoading: { table: { disable: true } }, + hasColumnDividers: { table: { disable: true } }, + hasMenu: { table: { disable: true } }, + isSortable: { table: { disable: true } }, + sortIcon: { table: { disable: true } }, + isDropTarget: { table: { disable: true } }, + useDivs: { table: { disable: true } }, + useScroller: { table: { disable: true } }, +}; /** * A table may render a [progress circle](/docs/components-progress-circle--docs) while populating data. @@ -421,7 +437,7 @@ export const Sortable = Template.bind({}); Sortable.args = { ...Default.args, isSortable: true, - sortIcon: "SortUp", + sortIcon: "Sort", }; Sortable.parameters = { chromatic: { disableSnapshot: true }, @@ -430,14 +446,14 @@ Sortable.storyName = "Sortable columns"; Sortable.tags = ["!dev"]; /** - * Tables can have additional actions in the header cells that could trigger a menu. The content of the header cell is an action button](/docs/components-action-button--docs). + * Tables can have additional actions in the header cells that could trigger a menu. The content of the header cell is an [button](/docs/components-button--docs). Oftentimes, the menu is used to show users the available sorting options. */ export const WithMenuButton = Template.bind({}); WithMenuButton.args = { ...Default.args, hasMenu: true, }; -WithMenuButton.storyName = "With action button"; +WithMenuButton.storyName = "With menu button"; WithMenuButton.parameters = { chromatic: { disableSnapshot: true }, }; @@ -549,6 +565,7 @@ SectionHeaderQuiet.storyName = "Section headers: quiet styling"; SectionHeaderQuiet.parameters = { chromatic: { disableSnapshot: true }, }; +SectionHeaderQuiet.tags = ["!dev"]; /** * A table can be wrapped in a fixed height `div` with the `.spectrum-Table-scroller` class. This allows scrolling of the table body and makes the column headers sticky (i.e. fixed to the top on scroll). From 6fbeb3e3d92251b16c7e7afb76cb2c1ac8833fbb Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Tue, 3 Jun 2025 14:28:24 -0400 Subject: [PATCH 41/52] chore(table): sort and menu button markup - removes action button markup in favor of regular button - adjusts template for sort-only and menu buttons - updates some class names - sort-only renders a button in the head cell, and menu renders a button in the head cell with the sort icon and disclosure icon --- components/table/stories/template.js | 45 +++++++++++++++------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/components/table/stories/template.js b/components/table/stories/template.js index c80a7a9dacf..cc7da374b44 100644 --- a/components/table/stories/template.js +++ b/components/table/stories/template.js @@ -1,4 +1,3 @@ -import { Template as ActionButton } from "@spectrum-css/actionbutton/stories/template.js"; import { Template as Avatar } from "@spectrum-css/avatar/stories/template.js"; import { Template as Button } from "@spectrum-css/button/stories/template.js"; import { Template as Checkbox } from "@spectrum-css/checkbox/stories/template.js"; @@ -315,34 +314,38 @@ export const Template = ({ ["is-sortable"]: isSortable, ["is-sorted-asc"]: sortIcon === "SortUp", ["is-sorted-desc"]: sortIcon === "SortDown", + [`${rootClass}-menuButton`]: hasMenu, })} role=${ifDefined(useDivs ? "columnheader" : undefined)} aria-sort=${ifDefined(isSortable ? ariaSortValue : undefined)} - tabindex=${ifDefined(isSortable ? "0" : undefined)} > - ${when(isSortable, () => Icon({ - iconName: sortIcon, - setName: "workflow", - customClasses: [`${rootClass}-sortIcon`], - }, context))} - Column title + ${when(isSortable, + () => Button({ + size: "m", + iconName: sortIcon, + iconSet: "workflow", + label: "Column title", + customClasses: [`${rootClass}-tableButton`], + }, context), + () => when(hasMenu, + () => Button({ + size: "m", + iconName: "SortUp", + iconSet: "workflow", + label: "Column title", + trailingIcon: "Chevron100", + trailingIconSet: "ui", + customClasses: [`${rootClass}-tableButton`], + }, context), + () => html`Column title` + ) + )} <${thTag} - class="${rootClass}-headCell ${hasMenu ? `${rootClass}-menuButton` : ""}" + class="${rootClass}-headCell" role=${ifDefined(useDivs ? "columnheader" : undefined)} > - ${when(hasMenu, () => ActionButton({ - size: "m", - isQuiet: true, - iconName: "ChevronDown100", - iconSet: "ui", - iconOnly: true, - label: "Column title", - customClasses: [`${rootClass}-menuButton`], - }, context), - () => html` - Column title - `)} + Column title <${thTag} class="${rootClass}-headCell" From a305b4ddff3bbd7e4c44fc47e63c990888baac71 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Tue, 3 Jun 2025 14:33:15 -0400 Subject: [PATCH 42/52] fix(table): combine some menu button styles - the sort-only and menu button styles are now the same since the markup changed - new passthrough mods for the button component were refactored from the action button component - removes unused sortIcon and menuIcon selectors/styles --- components/table/index.css | 129 +++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 61 deletions(-) diff --git a/components/table/index.css b/components/table/index.css index ff59fe25b46..3197ee5dd2b 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -114,20 +114,24 @@ --mod-avatar-block-size: var(--mod-table-avatar-size, var(--spectrum-table-avatar-size)); --mod-avatar-inline-size: var(--mod-table-avatar-size, var(--spectrum-table-avatar-size)); - /* Action button for menus in header cells */ - --mod-action-button-icon-size: var(--spectrum-table-disclosure-icon-size); - --mod-actionbutton-font-weight: var(--spectrum-table-header-font-weight); - --mod-actionbutton-font-size: var(--spectrum-table-row-font-size); - --mod-actionbutton-font-family: var(--spectrum-table-row-font-family); - --mod-actionbutton-line-height: var(--spectrum-table-row-line-height); - --mod-actionbutton-background-color-hover: transparent; - --mod-actionbutton-background-color-focus: transparent; - --mod-actionbutton-background-color-down: transparent; - --mod-actionbutton-content-color-default: var(--spectrum-table-header-text-color); - --mod-actionbutton-edge-to-text: var(--spectrum-table-cell-inline-spacing); - --mod-actionbutton-text-to-visual: var(--spectrum-table-visual-to-text); - --mod-actionbutton-edge-to-visual: var(--spectrum-table-cell-inline-spacing); - --mod-actionbutton-focus-indicator-border-radius: var(--spectrum-table-focused-cell-border-radius); + /* Button for menus in header cells */ + --mod-button-border-radius: 0; + --mod-button-font-weight: var(--spectrum-table-header-font-weight); + --mod-button-font-size: var(--spectrum-table-row-font-size); + --mod-button-font-family: var(--spectrum-table-row-font-family); + --mod-button-line-height: var(--spectrum-table-row-line-height); + --mod-button-background-color-default: transparent; + --mod-button-background-color-hover: transparent; + --mod-button-background-color-focus: transparent; + --mod-button-background-color-down: transparent; + --mod-button-content-color-default: var(--spectrum-table-header-text-color); + --mod-button-content-color-hover: var(--spectrum-table-header-text-color); + --mod-button-content-color-focus: var(--spectrum-table-header-text-color); + --mod-button-content-color-down: var(--spectrum-table-header-text-color); + --mod-button-edge-to-text: var(--spectrum-table-cell-inline-spacing); + --mod-button-padding-label-to-icon: var(--spectrum-table-visual-to-text); + --mod-button-height: var(--spectrum-table-min-header-row-height); + --mod-button-focus-ring-border-radius: var(--spectrum-table-focused-cell-border-radius); &:dir(rtl) { --spectrum-logical-rotation: matrix(-1, 0, 0, 1, 0, 0); @@ -215,7 +219,8 @@ } .spectrum-Table-headCell { - &.is-sortable { + &.is-sortable, + &.spectrum-Table-menuButton { &:hover { --spectrum-table-icon-color: var(--highcontrast-table-icon-color-focus, var(--mod-table-icon-color-hover, var(--spectrum-table-icon-color-hover))); } @@ -244,25 +249,6 @@ --spectrum-table-header-row-top-to-text: 0; --spectrum-table-header-row-bottom-to-text: 0; } - - /* These styles get applied to the icon within that menu button. */ - & .spectrum-Table-menuButton { - --mod-icon-color: var(--spectrum-table-icon-color); - - &:hover { - --mod-icon-color: var(--spectrum-table-icon-color-hover); - } - - &:active { - --mod-icon-color: var(--spectrum-table-icon-color-active); - } - - &:focus, - &:focus:hover, - &:focus-visible { - --mod-icon-color: var(--spectrum-table-icon-color-key-focus); - } - } } .spectrum-Table-scroller { @@ -354,43 +340,64 @@ } } -.spectrum-Table-sortIcon { - &:last-child .spectrum-Table-headCell { - border-start-end-radius: var(--mod-table-border-radius, var(--spectrum-table-border-radius)); - } -} - +/********* ICONS- SORT, DISCLOSURE/MENU *********/ /* Head cell column text */ .spectrum-Table-columnTitle { display: inline-block; vertical-align: middle; } -/* Head cell column text in the action button */ -.spectrum-Table-menuButton { - .spectrum-ActionButton { - flex-direction: row-reverse; - min-block-size: var(--spectrum-table-min-header-row-height); +/* A "sortable-only" head cell has no padding around its button. */ +.spectrum-Table-headCell.is-sortable { + padding: 0; - /* Allows the action button to wrap to the next line if the text + icon size is too long. */ - min-inline-size: unset; - flex-wrap: wrap-reverse; + /* Focus indicator- brings the focus indicator above the border and matches the table cell dimensions. */ + .spectrum-Table-tableButton::after { + inset: calc(3 * var(--mod-table-border-width, var(--spectrum-table-border-width))); + z-index: 1; } -} -/********* ICONS- SORT, DISCLOSURE/MENU *********/ -.spectrum-Table-sortIcon { - vertical-align: var(--mod-table-header-vertical-align, var(--spectrum-table-default-vertical-align)); - margin-inline-start: var(--mod-table-sort-icon-inline-start-spacing, 0); - margin-inline-end: var(--mod-table-sort-icon-inline-end-spacing, var(--mod-table-visual-to-text, var(--spectrum-table-visual-to-text))); - transition: transform var(--highcontrast-table-transition-duration, var(--mod-table-transition-duration, var(--spectrum-table-transition-duration))) ease-in-out; - color: var(--spectrum-table-icon-color); - display: inline-block; + .spectrum-Table-tableButton .spectrum-Icon { + color: var(--spectrum-table-icon-color); + transition: transform var(--highcontrast-table-transition-duration, var(--mod-table-transition-duration, var(--spectrum-table-transition-duration))) ease-in-out; + } } -.spectrum-Table-menuIcon { - vertical-align: middle; - margin-inline-start: var(--mod-table-visual-to-text, var(--spectrum-table-visual-to-text)); +/* Head cell column text in the button that opens the menu */ +.spectrum-Table-headCell.spectrum-Table-menuButton { + .spectrum-Table-tableButton { + min-block-size: calc(var(--spectrum-table-min-header-row-height) + var(--mod-table-border-width, var(--spectrum-table-border-width))); + + /* Focus indicator- brings the focus indicator above the border and matches the table cell dimensions. */ + &::after { + /* Multiply by 3 to account for the borders on the top, side and bottom of the table cell. */ + inset: calc(3 * var(--mod-table-border-width, var(--spectrum-table-border-width))); + z-index: 1; + } + + .spectrum-Button-label { + vertical-align: middle; + display: inline-block; + } + + .spectrum-Button-label::after { + content: ""; + display: inline-block; + mask-image: url("ui-icons/dist/svg/Chevron100.svg"); + mask-size: contain; + block-size: 10px; + inline-size: 10px; + background-color: var(--spectrum-table-icon-color); + transform: rotate(90deg); + vertical-align: middle; + margin-inline-start: var(--mod-table-visual-to-text, var(--spectrum-table-visual-to-text)); + } + + & .spectrum-Icon { + transition: transform var(--highcontrast-table-transition-duration, var(--mod-table-transition-duration, var(--spectrum-table-transition-duration))) ease-in-out; + color: var(--spectrum-table-icon-color); + } + } } /********* TEXT ALIGNMENT *********/ @@ -560,7 +567,7 @@ border-block: var(--spectrum-table-border-width) solid var(--highcontrast-table-focus-indicator-color, var(--mod-table-focus-indicator-color, var(--spectrum-table-focus-indicator-color))); /* Because the ::before element technically removes the table's inline border, the first cell needs the entire cell padding to avoid horizontal layout shift when a row is focused. */ - padding-inline-start: var(--mod-table-cell-inline-spacing,var(--spectrum-table-cell-inline-spacing)); + padding-inline-start: var(--mod-table-cell-inline-spacing, var(--spectrum-table-cell-inline-spacing)); /* The side row focus indicator line */ &::before { From 8194a6a7fb13ab96b4401659c70c9574e005d890 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Tue, 3 Jun 2025 14:32:49 -0400 Subject: [PATCH 43/52] chore(table): update metadata --- components/table/dist/metadata.json | 57 ++++++++++++++++------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/components/table/dist/metadata.json b/components/table/dist/metadata.json index 2fd6a4105b4..6f7fefc17c2 100644 --- a/components/table/dist/metadata.json +++ b/components/table/dist/metadata.json @@ -41,14 +41,11 @@ ".spectrum-Table-head", ".spectrum-Table-head [role=\"row\"]", ".spectrum-Table-headCell", - ".spectrum-Table-headCell .spectrum-Table-menuButton", - ".spectrum-Table-headCell .spectrum-Table-menuButton:active", - ".spectrum-Table-headCell .spectrum-Table-menuButton:focus", - ".spectrum-Table-headCell .spectrum-Table-menuButton:focus-visible", - ".spectrum-Table-headCell .spectrum-Table-menuButton:focus:hover", - ".spectrum-Table-headCell .spectrum-Table-menuButton:hover", ".spectrum-Table-headCell.is-focused", ".spectrum-Table-headCell.is-focused:after", + ".spectrum-Table-headCell.is-sortable", + ".spectrum-Table-headCell.is-sortable .spectrum-Table-tableButton .spectrum-Icon", + ".spectrum-Table-headCell.is-sortable .spectrum-Table-tableButton:after", ".spectrum-Table-headCell.is-sortable.is-keyboardFocused", ".spectrum-Table-headCell.is-sortable:active", ".spectrum-Table-headCell.is-sortable:focus", @@ -56,6 +53,17 @@ ".spectrum-Table-headCell.is-sortable:focus:hover", ".spectrum-Table-headCell.is-sortable:hover", ".spectrum-Table-headCell.spectrum-Table-menuButton", + ".spectrum-Table-headCell.spectrum-Table-menuButton .spectrum-Table-tableButton", + ".spectrum-Table-headCell.spectrum-Table-menuButton .spectrum-Table-tableButton .spectrum-Button-label", + ".spectrum-Table-headCell.spectrum-Table-menuButton .spectrum-Table-tableButton .spectrum-Button-label:after", + ".spectrum-Table-headCell.spectrum-Table-menuButton .spectrum-Table-tableButton .spectrum-Icon", + ".spectrum-Table-headCell.spectrum-Table-menuButton .spectrum-Table-tableButton:after", + ".spectrum-Table-headCell.spectrum-Table-menuButton.is-keyboardFocused", + ".spectrum-Table-headCell.spectrum-Table-menuButton:active", + ".spectrum-Table-headCell.spectrum-Table-menuButton:focus", + ".spectrum-Table-headCell.spectrum-Table-menuButton:focus-visible", + ".spectrum-Table-headCell.spectrum-Table-menuButton:focus:hover", + ".spectrum-Table-headCell.spectrum-Table-menuButton:hover", ".spectrum-Table-headCell:focus-visible", ".spectrum-Table-headCell:focus-visible:after", ".spectrum-Table-headRow .spectrum-ActionButton:after", @@ -66,8 +74,6 @@ ".spectrum-Table-headRow:first-child .spectrum-Table-headCell:first-child", ".spectrum-Table-headRow:last-child .spectrum-Table-headCell:last-child", ".spectrum-Table-main", - ".spectrum-Table-menuButton .spectrum-ActionButton", - ".spectrum-Table-menuIcon", ".spectrum-Table-row", ".spectrum-Table-row .is-emphasized", ".spectrum-Table-row .spectrum-Table-cell", @@ -139,8 +145,6 @@ ".spectrum-Table-scroller .spectrum-Table-head", ".spectrum-Table-scroller .spectrum-Table-headRow:first-child .spectrum-Table-headCell", ".spectrum-Table-scroller.spectrum-Table--quiet", - ".spectrum-Table-sortIcon", - ".spectrum-Table-sortIcon:last-child .spectrum-Table-headCell", ".spectrum-Table-visualInner", ".spectrum-Table-visualInner .spectrum-Avatar", ".spectrum-Table-visualInner .spectrum-Icon", @@ -227,8 +231,6 @@ "--mod-table-selected-row-background-color-focus", "--mod-table-selected-row-background-color-non-emphasized", "--mod-table-selected-row-background-color-non-emphasized-focus", - "--mod-table-sort-icon-inline-end-spacing", - "--mod-table-sort-icon-inline-start-spacing", "--mod-table-summary-row-background-color", "--mod-table-summary-row-bottom-to-text", "--mod-table-summary-row-font-family", @@ -384,23 +386,26 @@ "--spectrum-transparent-white-25" ], "passthroughs": [ - "--mod-action-button-icon-size", - "--mod-actionbutton-background-color-down", - "--mod-actionbutton-background-color-focus", - "--mod-actionbutton-background-color-hover", - "--mod-actionbutton-content-color-default", - "--mod-actionbutton-edge-to-text", - "--mod-actionbutton-edge-to-visual", - "--mod-actionbutton-focus-indicator-border-radius", - "--mod-actionbutton-font-family", - "--mod-actionbutton-font-size", - "--mod-actionbutton-font-weight", - "--mod-actionbutton-line-height", - "--mod-actionbutton-text-to-visual", "--mod-avatar-block-size", "--mod-avatar-inline-size", + "--mod-button-background-color-default", + "--mod-button-background-color-down", + "--mod-button-background-color-focus", + "--mod-button-background-color-hover", + "--mod-button-border-radius", + "--mod-button-content-color-default", + "--mod-button-content-color-down", + "--mod-button-content-color-focus", + "--mod-button-content-color-hover", + "--mod-button-edge-to-text", + "--mod-button-focus-ring-border-radius", + "--mod-button-font-family", + "--mod-button-font-size", + "--mod-button-font-weight", + "--mod-button-height", + "--mod-button-line-height", + "--mod-button-padding-label-to-icon", "--mod-checkbox-margin-block", - "--mod-icon-color", "--mod-thumbnail-size" ], "high-contrast": [ From 174f72f949280937875f323d44085af6116a17f0 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 4 Jun 2025 12:12:46 -0400 Subject: [PATCH 44/52] test(table): add/fixes tests for selection mode and menu button --- components/table/stories/table.test.js | 39 ++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/components/table/stories/table.test.js b/components/table/stories/table.test.js index 9c38409442e..3d0eba27f69 100644 --- a/components/table/stories/table.test.js +++ b/components/table/stories/table.test.js @@ -147,16 +147,47 @@ export const TableGroup = Variants({ { testHeading: "Selection mode: multiple, emphasized", rowItems: ExampleMultiSelectContent, + selectionMode: "multiple", + }, + { + testHeading: "Selection mode: single, emphasized", + rowItems: [ + { + cellContent: ["Pikachu", "Electric", "35"], + textAlignment: { + 2: "end" + }, + showCheckbox: true, + isSelected: true, + isChecked: true, + }, + { + cellContent: ["Charmander", "Fire", "39"], + textAlignment: { + 2: "end" + }, + showCheckbox: true, + }, + { + cellContent: ["Mew", "Psychic", "100"], + textAlignment: { + 2: "end" + }, + showCheckbox: true, + } + ], }, { testHeading: "Selection mode: multiple, non-emphasized", rowItems: ExampleMultiSelectContent, isEmphasized: false, + selectionMode: "multiple", }, { - testHeading: "Quiet multi-select: emphasized", + testHeading: "Selection mode: multiple, quiet, emphasized", isQuiet: true, rowItems: ExampleMultiSelectContent, + selectionMode: "multiple", }, { testHeading: "Scrollable", @@ -170,9 +201,13 @@ export const TableGroup = Variants({ rowItems: ExampleMultiSelectContent, }, { - testHeading: "Sortable columns: Sort", + testHeading: "Sortable columns", isSortable: true, }, + { + testHeading: "Head cell with menu button", + hasMenu: true, + }, { testHeading: "Section headers", rowItems: ExampleSectionHeadersContent, From fdc1c9fd0dbfacda8bb20d8670206b5b6046191f Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 4 Jun 2025 12:39:47 -0400 Subject: [PATCH 45/52] fix(table): fix checkbox margin in head cells --- components/table/index.css | 5 ----- 1 file changed, 5 deletions(-) diff --git a/components/table/index.css b/components/table/index.css index 3197ee5dd2b..5a4d2389691 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -675,11 +675,6 @@ min-block-size: initial; } - - &.spectrum-Table-headCell .spectrum-Table-checkbox { - margin-block-start: calc(var(--mod-table-header-checkbox-block-spacing, var(--spectrum-table-header-row-checkbox-block-spacing)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); - margin-block-end: var(--mod-table-header-checkbox-block-spacing, var(--spectrum-table-header-row-checkbox-block-spacing)); - } } /********* DIVS *********/ From b70b4caafcabd3378e03621d094966c2c8da03df Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 4 Jun 2025 12:50:37 -0400 Subject: [PATCH 46/52] fix(table): fix section header padding --- components/table/index.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/table/index.css b/components/table/index.css index 5a4d2389691..a176bb86d0c 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -646,7 +646,7 @@ block-size: var(--mod-table-section-header-min-height, var(--spectrum-table-section-header-min-height)); padding-block-start: calc(var(--mod-table-section-header-block-start-spacing, var(--spectrum-table-section-header-block-start-spacing)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); - padding-block-end: calc(var(--mod-table-section-header-block-end-spacing, var(--spectrum-table-section-header-block-end-spacing)) - var(--mod-table-border-width, var(--spectrum-table-border-width))); + padding-block-end: var(--mod-table-section-header-block-end-spacing, var(--spectrum-table-section-header-block-end-spacing)); /* Make unique section header mods available to these default row styles: */ padding-inline-start: calc(var(--mod-table-section-header-inline-start-spacing, var(--spectrum-table-cell-inline-spacing)) - var(--mod-table-outer-border-inline-width, var(--spectrum-table-outer-border-inline-width))); From 81a8d2e13a102a4933d3aae8ca7ab574b75e86a1 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 4 Jun 2025 12:54:19 -0400 Subject: [PATCH 47/52] chore(table): update metadata --- components/table/dist/metadata.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/table/dist/metadata.json b/components/table/dist/metadata.json index 6f7fefc17c2..566b168c9da 100644 --- a/components/table/dist/metadata.json +++ b/components/table/dist/metadata.json @@ -26,7 +26,6 @@ ".spectrum-Table-cell:focus-visible:after", ".spectrum-Table-checkboxCell", ".spectrum-Table-checkboxCell .spectrum-Table-checkbox", - ".spectrum-Table-checkboxCell.spectrum-Table-headCell .spectrum-Table-checkbox", ".spectrum-Table-collapseInner", ".spectrum-Table-columnTitle", ".spectrum-Table-disclosureIcon.spectrum-Table-disclosureIcon", @@ -180,7 +179,6 @@ "--mod-table-header-background-color", "--mod-table-header-background-color-quiet", "--mod-table-header-background-color-scrollable", - "--mod-table-header-checkbox-block-spacing", "--mod-table-header-font-family", "--mod-table-header-font-size", "--mod-table-header-font-weight", From 98362dc5bd412f15b86182cefb06e003546250e0 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 5 Jun 2025 09:45:04 -0400 Subject: [PATCH 48/52] chore(table): updates for docs and template - remove trailingIcon arg - correctly adds aria-multiselectable to the table when the selectionMode is set to "multiple" - updates to docs page --- components/table/stories/table.stories.js | 11 +++++------ components/table/stories/template.js | 4 +--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index 7c5f1671696..c603bfbdf59 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -199,8 +199,6 @@ const ExampleRowItems = [ /** * The default table also uses the regular density. Similar to a paragraph of text, textual data is always start-aligned within a table. Never use center alignment. - * - * Tables with sortable columns can show different states of sorting: unsorted, ascending, and descending. Additionally, tables can also trigger a menu, as indicated by the chevron. */ export const Default = TableGroup.bind({}); Default.args = {}; @@ -274,7 +272,8 @@ Spacious.storyName = "Density - spacious"; /** * The standard multi-select table includes a column of checkboxes used for selecting rows. When the selection mode - * is set to `multiple`, users may select more than one table row. + * is set to `multiple`, users may select more than one table row. Typically, an [indeterminate checkbox](/docs/components-checkbox--docs) + * is used to indicate that ability. */ export const MultiSelect = Template.bind({}); MultiSelect.storyName = "Selection mode: multiple"; @@ -427,11 +426,11 @@ Quiet.parameters = { }; /** - * Tables can enable column sorting. The `aria-sort` attribute should be set on the `.spectrum-Table-headCell` element for the column that is currently sorted. `aria-sort` should not be set on more than one column at a time. + * Tables with enabled column sorting can show different states of sorting: unsorted, ascending, and descending. Additionally, tables can trigger a menu, as indicated by the chevron. The `aria-sort` attribute should be set on the `.spectrum-Table-headCell` element for the column that is currently sorted. `aria-sort` should not be set on more than one column at a time. * * When a column is sorted in ascending order, use the `SortUp` icon, as seen below. If sorted in descending order, use the `SortDown` icon. If the sort is undefined, use the more general `Sort` icon. * - * Implementations will develop their own sorting functionality. Additionally, the `aria-sort` attribute should be removed from a column header when the column is not sorted. + * Implementations will develop their own sorting functionality. The `aria-sort` attribute should be removed from a column header when the column is not sorted. */ export const Sortable = Template.bind({}); Sortable.args = { @@ -611,7 +610,7 @@ Scrollable.parameters = { }; /** - * A table can also be made up of `div` tags if needed, instead of a `
`. This example uses both the div markup, and the scrollable wrapper. + * A table can also be made up of `div` tags if needed, instead of a `
`. This example uses both the `div` markup, and the scrollable wrapper. */ export const DivsScrollable = Template.bind({}); DivsScrollable.storyName = "Scrollable table with divs"; diff --git a/components/table/stories/template.js b/components/table/stories/template.js index cc7da374b44..675c6c51392 100644 --- a/components/table/stories/template.js +++ b/components/table/stories/template.js @@ -282,7 +282,7 @@ export const Template = ({ })} id=${ifDefined(id)} role=${ifDefined(useCheckboxCell ? "grid" : useDivs ? "table" : undefined)} - aria-multiselectable=${ifDefined(useCheckboxCell ? "true" : undefined)} + aria-multiselectable=${ifDefined(selectionMode === "multiple" ? "true" : undefined)} > <${theadTag} class="${rootClass}-head" @@ -333,8 +333,6 @@ export const Template = ({ iconName: "SortUp", iconSet: "workflow", label: "Column title", - trailingIcon: "Chevron100", - trailingIconSet: "ui", customClasses: [`${rootClass}-tableButton`], }, context), () => html`Column title` From 9079f57d4ad89303520a2d13db4c27b0abf54909 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 5 Jun 2025 10:39:58 -0400 Subject: [PATCH 49/52] chore(table): fix some changeset wording --- .changeset/petite-toys-greet.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/petite-toys-greet.md b/.changeset/petite-toys-greet.md index 0a06b9bb50b..b085079cfa0 100644 --- a/.changeset/petite-toys-greet.md +++ b/.changeset/petite-toys-greet.md @@ -10,14 +10,14 @@ Compared to the S1 table, this component has updated corner rounding, updated co - The S2 table supports an empty state, rendering an illustrated message component. - As data is loading to the table, this component will render a progress circle during the loading state. -- There are 2 selection modes: single-select and multi-select. Multi-select tables (`selectionMode: "multiple"`) render an indeterminate checkbox in the `thead`/`div` equivalent. Single-select tables (`selectionMode: "single"`) do not render the indeterminate checkbox in the header row. +- There are 2 selection modes: single-select and multi-select. Multi-select tables (`selectionMode: "multiple"`) render an indeterminate checkbox in the `thead`/`div` equivalent. Single-select tables (`selectionMode: "single"`) do not render the indeterminate checkbox in the header row. (Note: the `selectionMode` arg is disabled and will not render in the Storybook control table.) - For tables with sortable column, there are three new S2 icons used: `Sort` to indicate "general" sorting, `SortUp` for ascending sort direction, `SortDown` for descending sort direction. - Tables support thumbnail, avatar, and icon components as content within a cell. - Focus indicators for entire rows have been updated for rows to include a side focus indicator. #### Description of other S2 table work -T-shirt sizing for tables is not technically supported, so t-shirt size classes (i.e. `.spectrum-Table--sizeS`), has been removed across all density variants. +T-shirt sizing for tables is not technically supported, so t-shirt size classes (i.e. `.spectrum-Table--sizeS`), have been removed across all density variants. The `.spectrum-Table-cell--alignRight` class has been renamed to `.spectrum-Table-cell--alignEnd` to reflect the preference for "logical" positioning. The `.spectrum-Table-cell--alignCenter` class has been refactored to `.spectrum-Table-cell--alignStart`. From 039f3fbaaf98f68f56aad10ee59ac4930158b0f9 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 5 Jun 2025 11:23:51 -0400 Subject: [PATCH 50/52] fix(table): refactor mask-image path to chevron svg --- components/table/index.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/table/index.css b/components/table/index.css index a176bb86d0c..703cc0937b4 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -383,8 +383,11 @@ .spectrum-Button-label::after { content: ""; display: inline-block; - mask-image: url("ui-icons/dist/svg/Chevron100.svg"); + mask-image: url('data:image/svg+xml,'); mask-size: contain; + mask-repeat: no-repeat; + mask-position: center; + block-size: 10px; inline-size: 10px; background-color: var(--spectrum-table-icon-color); From f5548291cbd46962d46caff584288d9b19214b2b Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 5 Jun 2025 16:02:28 -0400 Subject: [PATCH 51/52] fix(table): pr fixes - updates to documentation to clear up additional classes for the sort button and menu button - updates the template to use the new classes - updates the aria sort attribute to be set on the head cell if hasMenu is true - updates the new classes in the CSS --- components/table/index.css | 14 +++++----- components/table/stories/table.stories.js | 10 +++++-- components/table/stories/template.js | 34 +++++++++++------------ 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/components/table/index.css b/components/table/index.css index 703cc0937b4..3fd1f5f836b 100644 --- a/components/table/index.css +++ b/components/table/index.css @@ -220,7 +220,7 @@ .spectrum-Table-headCell { &.is-sortable, - &.spectrum-Table-menuButton { + &.spectrum-Table-hasMenuButton { &:hover { --spectrum-table-icon-color: var(--highcontrast-table-icon-color-focus, var(--mod-table-icon-color-hover, var(--spectrum-table-icon-color-hover))); } @@ -243,8 +243,8 @@ } } - /* These styles get applied to the headCell when the headCell has the --menuButton modifier class. */ - &.spectrum-Table-menuButton { + /* These styles get applied to the headCell when the headCell has the --hasMenuButton class. */ + &.spectrum-Table-hasMenuButton { --spectrum-table-cell-inline-spacing: 0; --spectrum-table-header-row-top-to-text: 0; --spectrum-table-header-row-bottom-to-text: 0; @@ -352,20 +352,20 @@ padding: 0; /* Focus indicator- brings the focus indicator above the border and matches the table cell dimensions. */ - .spectrum-Table-tableButton::after { + .spectrum-Table-sortButton::after { inset: calc(3 * var(--mod-table-border-width, var(--spectrum-table-border-width))); z-index: 1; } - .spectrum-Table-tableButton .spectrum-Icon { + .spectrum-Table-sortButton .spectrum-Icon { color: var(--spectrum-table-icon-color); transition: transform var(--highcontrast-table-transition-duration, var(--mod-table-transition-duration, var(--spectrum-table-transition-duration))) ease-in-out; } } /* Head cell column text in the button that opens the menu */ -.spectrum-Table-headCell.spectrum-Table-menuButton { - .spectrum-Table-tableButton { +.spectrum-Table-headCell.spectrum-Table-hasMenuButton { + .spectrum-Table-sortButton { min-block-size: calc(var(--spectrum-table-min-header-row-height) + var(--mod-table-border-width, var(--spectrum-table-border-width))); /* Focus indicator- brings the focus indicator above the border and matches the table cell dimensions. */ diff --git a/components/table/stories/table.stories.js b/components/table/stories/table.stories.js index c603bfbdf59..ff16c7518bb 100644 --- a/components/table/stories/table.stories.js +++ b/components/table/stories/table.stories.js @@ -426,10 +426,12 @@ Quiet.parameters = { }; /** - * Tables with enabled column sorting can show different states of sorting: unsorted, ascending, and descending. Additionally, tables can trigger a menu, as indicated by the chevron. The `aria-sort` attribute should be set on the `.spectrum-Table-headCell` element for the column that is currently sorted. `aria-sort` should not be set on more than one column at a time. + * Tables with enabled column sorting can show different states of sorting: unsorted, ascending, and descending. The content of the header cell is an [button](/docs/components-button--docs), that has a class of `.spectrum-Table-sortButton`, which should contain the sorting icon, as well as the header label. * * When a column is sorted in ascending order, use the `SortUp` icon, as seen below. If sorted in descending order, use the `SortDown` icon. If the sort is undefined, use the more general `Sort` icon. * + * The `aria-sort` attribute should be set on the `.spectrum-Table-headCell .is-sortable` element for the column that is currently sorted, and when sorting is active. `aria-sort` should not be set on more than one column at a time. + * * Implementations will develop their own sorting functionality. The `aria-sort` attribute should be removed from a column header when the column is not sorted. */ export const Sortable = Template.bind({}); @@ -445,14 +447,16 @@ Sortable.storyName = "Sortable columns"; Sortable.tags = ["!dev"]; /** - * Tables can have additional actions in the header cells that could trigger a menu. The content of the header cell is an [button](/docs/components-button--docs). Oftentimes, the menu is used to show users the available sorting options. + * Tables can have additional actions in the header cells that could trigger a menu, as indicated by the chevron. Oftentimes, the menu is used to show users the available sorting options. The content of the header cell is an [button](/docs/components-button--docs), that has a class of `.spectrum-Table-sortButton`, which should contain the sorting icon when applicable, as well as the header label. + * + * The `aria-sort` attribute should be set on the `.spectrum-Table-hasMenuButton` element for the column that is currently sorted, and when sorting is active. */ export const WithMenuButton = Template.bind({}); WithMenuButton.args = { ...Default.args, hasMenu: true, }; -WithMenuButton.storyName = "With menu button"; +WithMenuButton.storyName = "Sortable columns: with menu button"; WithMenuButton.parameters = { chromatic: { disableSnapshot: true }, }; diff --git a/components/table/stories/template.js b/components/table/stories/template.js index 675c6c51392..f90d4e1e0e8 100644 --- a/components/table/stories/template.js +++ b/components/table/stories/template.js @@ -314,30 +314,30 @@ export const Template = ({ ["is-sortable"]: isSortable, ["is-sorted-asc"]: sortIcon === "SortUp", ["is-sorted-desc"]: sortIcon === "SortDown", - [`${rootClass}-menuButton`]: hasMenu, + [`${rootClass}-hasMenuButton`]: hasMenu, })} role=${ifDefined(useDivs ? "columnheader" : undefined)} - aria-sort=${ifDefined(isSortable ? ariaSortValue : undefined)} + aria-sort=${ifDefined(isSortable || hasMenu ? ariaSortValue : undefined)} > - ${when(isSortable, - () => Button({ - size: "m", - iconName: sortIcon, - iconSet: "workflow", - label: "Column title", - customClasses: [`${rootClass}-tableButton`], - }, context), - () => when(hasMenu, - () => Button({ + ${when(hasMenu || isSortable, () => html` + ${when(isSortable, () => Button({ + size: "m", + iconName: sortIcon, + iconSet: "workflow", + label: "Column title", + customClasses: [`${rootClass}-sortButton`], + }, context) + )} + ${when(!isSortable, () => Button({ size: "m", iconName: "SortUp", iconSet: "workflow", label: "Column title", - customClasses: [`${rootClass}-tableButton`], - }, context), - () => html`Column title` - ) - )} + customClasses: [`${rootClass}-sortButton`], + }, context) + )} + `, () => html`Column title` + )} <${thTag} class="${rootClass}-headCell" From 101b69b1c607216cacf3db6a4ee45dbb3c2a70d8 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 5 Jun 2025 16:05:18 -0400 Subject: [PATCH 52/52] chore(table): update metadata --- components/table/dist/metadata.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/components/table/dist/metadata.json b/components/table/dist/metadata.json index 566b168c9da..277dc9d978c 100644 --- a/components/table/dist/metadata.json +++ b/components/table/dist/metadata.json @@ -43,26 +43,26 @@ ".spectrum-Table-headCell.is-focused", ".spectrum-Table-headCell.is-focused:after", ".spectrum-Table-headCell.is-sortable", - ".spectrum-Table-headCell.is-sortable .spectrum-Table-tableButton .spectrum-Icon", - ".spectrum-Table-headCell.is-sortable .spectrum-Table-tableButton:after", + ".spectrum-Table-headCell.is-sortable .spectrum-Table-sortButton .spectrum-Icon", + ".spectrum-Table-headCell.is-sortable .spectrum-Table-sortButton:after", ".spectrum-Table-headCell.is-sortable.is-keyboardFocused", ".spectrum-Table-headCell.is-sortable:active", ".spectrum-Table-headCell.is-sortable:focus", ".spectrum-Table-headCell.is-sortable:focus-visible", ".spectrum-Table-headCell.is-sortable:focus:hover", ".spectrum-Table-headCell.is-sortable:hover", - ".spectrum-Table-headCell.spectrum-Table-menuButton", - ".spectrum-Table-headCell.spectrum-Table-menuButton .spectrum-Table-tableButton", - ".spectrum-Table-headCell.spectrum-Table-menuButton .spectrum-Table-tableButton .spectrum-Button-label", - ".spectrum-Table-headCell.spectrum-Table-menuButton .spectrum-Table-tableButton .spectrum-Button-label:after", - ".spectrum-Table-headCell.spectrum-Table-menuButton .spectrum-Table-tableButton .spectrum-Icon", - ".spectrum-Table-headCell.spectrum-Table-menuButton .spectrum-Table-tableButton:after", - ".spectrum-Table-headCell.spectrum-Table-menuButton.is-keyboardFocused", - ".spectrum-Table-headCell.spectrum-Table-menuButton:active", - ".spectrum-Table-headCell.spectrum-Table-menuButton:focus", - ".spectrum-Table-headCell.spectrum-Table-menuButton:focus-visible", - ".spectrum-Table-headCell.spectrum-Table-menuButton:focus:hover", - ".spectrum-Table-headCell.spectrum-Table-menuButton:hover", + ".spectrum-Table-headCell.spectrum-Table-hasMenuButton", + ".spectrum-Table-headCell.spectrum-Table-hasMenuButton .spectrum-Table-sortButton", + ".spectrum-Table-headCell.spectrum-Table-hasMenuButton .spectrum-Table-sortButton .spectrum-Button-label", + ".spectrum-Table-headCell.spectrum-Table-hasMenuButton .spectrum-Table-sortButton .spectrum-Button-label:after", + ".spectrum-Table-headCell.spectrum-Table-hasMenuButton .spectrum-Table-sortButton .spectrum-Icon", + ".spectrum-Table-headCell.spectrum-Table-hasMenuButton .spectrum-Table-sortButton:after", + ".spectrum-Table-headCell.spectrum-Table-hasMenuButton.is-keyboardFocused", + ".spectrum-Table-headCell.spectrum-Table-hasMenuButton:active", + ".spectrum-Table-headCell.spectrum-Table-hasMenuButton:focus", + ".spectrum-Table-headCell.spectrum-Table-hasMenuButton:focus-visible", + ".spectrum-Table-headCell.spectrum-Table-hasMenuButton:focus:hover", + ".spectrum-Table-headCell.spectrum-Table-hasMenuButton:hover", ".spectrum-Table-headCell:focus-visible", ".spectrum-Table-headCell:focus-visible:after", ".spectrum-Table-headRow .spectrum-ActionButton:after",