Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Fixes table editable cell styles and simplifies it #3061

Merged
merged 5 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/table/body-cell/inline-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export function InlineEditor<ItemType>({
aria-label={ariaLabels?.activateEditLabel?.(column, item)}
onKeyDown={handleEscape}
>
<form onSubmit={onSubmitClick} className={styles['body-cell-editor-form']}>
<form onSubmit={onSubmitClick}>
<FormField
stretch={true}
label={ariaLabel}
Expand Down
60 changes: 20 additions & 40 deletions src/table/body-cell/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ $edit-button-padding-right: calc(#{awsui.$space-xs} + #{awsui.$space-xxs});
// Cell vertical padding + xxs space that would normally come from the button.
$success-icon-padding-right: calc(#{$edit-button-padding-right} + #{$icon-width-with-spacing});
$interactive-column-padding-inline-end: calc(#{$cell-horizontal-padding} + #{awsui.$space-l});
$editing-cell-padding-inline: awsui.$space-xxs;
$editing-cell-padding-block: awsui.$space-scaled-xxxs;
$cell-offset: calc(#{awsui.$space-m} + #{awsui.$space-xs});

// Ensuring enough space for absolute-positioned focus outlines of focus-able cell content elements.
$cell-negative-space-vertical: 2px;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This negative space was used to virtually enlarge the cell-content element. That is because the element has overflow: hidden but when there are interactive elements inside such a link or a button dropdown - the focus outline is being cut off.

The negative space is no longer needed because now the cell content size matches cell size. This way the outline can use the available cell space while the content is still truncated correctly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: the negative space is still needed at least for the compact mode.


@mixin cell-focus-outline {
@include styles.focus-highlight(calc(-1 * #{awsui.$space-scaled-xxs}));

Expand Down Expand Up @@ -99,20 +98,17 @@ $cell-negative-space-vertical: 2px;
}
@mixin cell-padding-block($padding) {
> .body-cell-content {
padding-block: calc(#{$padding} - 1 * #{awsui.$border-divider-list-width} + #{$cell-negative-space-vertical});
margin-block: calc(-1 * #{$cell-negative-space-vertical});
padding-block: calc(#{$padding} - 1 * #{awsui.$border-divider-list-width});
}
}
@mixin cell-padding-block-start($padding) {
> .body-cell-content {
padding-block-start: calc(#{$padding} - 1 * #{awsui.$border-divider-list-width} + #{$cell-negative-space-vertical});
margin-block-start: calc(-1 * #{$cell-negative-space-vertical});
padding-block-start: calc(#{$padding} - 1 * #{awsui.$border-divider-list-width});
}
}
@mixin cell-padding-block-end($padding) {
> .body-cell-content {
padding-block-end: calc(#{$padding} - 1 * #{awsui.$border-divider-list-width} + #{$cell-negative-space-vertical});
margin-block-end: calc(-1 * #{$cell-negative-space-vertical});
padding-block-end: calc(#{$padding} - 1 * #{awsui.$border-divider-list-width});
}
}

Expand All @@ -131,11 +127,18 @@ $cell-negative-space-vertical: 2px;

&-content {
box-sizing: border-box;
}
&:not(.body-cell-wrap) > .body-cell-content {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
block-size: 100%;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making cell content span to the entire cell height.

align-content: center;

&.body-cell-align-top {
align-content: baseline;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now the cell content takes the entire cell size so the alignment must be attached to it instead of the cell.

}

&:not(.body-cell-wrap) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .body-cell-wrap only takes effect on the cell-content element so attaching the style to it directly.

white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
&:first-child {
border-inline-start: $border-placeholder;
Expand Down Expand Up @@ -177,20 +180,10 @@ $cell-negative-space-vertical: 2px;
&:not(.has-selection):not(.body-cell-editable) {
border-inline-start: none;
}

// Give extra space on the edge to allow slight content overflow.
// That is to prevent focus outline on cell content from being cut out.
> .body-cell-content {
padding-inline-start: awsui.$border-divider-list-width;
margin-inline-start: calc(-1 * #{awsui.$border-divider-list-width});
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This negative space shenanigans are no longer needed because there is plenty of space for the focus outline.

}
}
&:first-child:not(.is-visual-refresh) {
@include cell-padding-inline-start($cell-edge-horizontal-padding);
}
&-align-top {
vertical-align: top;
}
&-first-row {
border-block-start: $border-placeholder;
}
Expand Down Expand Up @@ -380,19 +373,6 @@ $cell-negative-space-vertical: 2px;
color: awsui.$color-text-button-normal-active;
}

&-form {
Copy link
Member Author

@pan-kot pan-kot Dec 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For editing cells we used overflow: visible on cell content together with negative margins so that the editor can take more space inside a cell and its focus outline is not cut off. Instead, in the refactored versions we just use different paddings for editing cells and there is no longer an issue with the outline.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: the overflow visible is still needed for editable content that includes dropdowns. I will contribute a new screenshot test to capture that.

margin-block: calc(-1 * #{awsui.$space-xs});
margin-inline: calc(-1.5 * #{awsui.$space-xs});

.is-visual-refresh.body-cell:first-child.has-striped-rows > & {
margin-inline-start: calc(-1 * #{awsui.$space-xxs});
}

.is-visual-refresh.body-cell:first-child:not(.has-striped-rows) > & {
margin-inline-start: calc(-1 * #{awsui.$space-xxxs});
}
}

&-row {
display: flex;
flex-flow: row nowrap;
Expand Down Expand Up @@ -428,9 +408,9 @@ $cell-negative-space-vertical: 2px;
}

&.body-cell-edit-active {
> .body-cell-content {
overflow: visible;
}
@include cell-padding-inline-start($editing-cell-padding-inline);
@include cell-padding-inline-end($editing-cell-padding-inline);
@include cell-padding-block($editing-cell-padding-block);
}

&:not(.body-cell-edit-active) {
Expand Down
21 changes: 15 additions & 6 deletions src/table/body-cell/td-element.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { copyAnalyticsMetadataAttribute } from '@cloudscape-design/component-too
import { useSingleTabStopNavigation } from '../../internal/context/single-tab-stop-navigation-context';
import { useMergeRefs } from '../../internal/hooks/use-merge-refs';
import { useVisualRefresh } from '../../internal/hooks/use-visual-mode';
import { ColumnWidthStyle } from '../column-widths-utils';
import { ExpandToggleButton } from '../expandable-rows/expand-toggle-button';
import { TableProps } from '../interfaces.js';
import { StickyColumnsModel, useStickyCellStyles } from '../sticky-columns';
Expand All @@ -18,7 +19,6 @@ import tableStyles from '../styles.css.js';
import styles from './styles.css.js';

export interface TableTdElementProps {
style?: React.CSSProperties;
wrapLines: boolean | undefined;
isRowHeader?: boolean;
isFirstRow: boolean;
Expand Down Expand Up @@ -51,6 +51,7 @@ export interface TableTdElementProps {
collapseButtonLabel?: string;
verticalAlign?: TableProps.VerticalAlign;
resizableColumns?: boolean;
resizableStyle?: ColumnWidthStyle;
isEditable: boolean;
isEditing: boolean;
isEditingDisabled?: boolean;
Expand All @@ -60,7 +61,6 @@ export interface TableTdElementProps {
export const TableTdElement = React.forwardRef<HTMLTableCellElement, TableTdElementProps>(
(
{
style,
children,
wrapLines,
isRowHeader,
Expand Down Expand Up @@ -90,6 +90,7 @@ export const TableTdElement = React.forwardRef<HTMLTableCellElement, TableTdElem
collapseButtonLabel,
verticalAlign,
resizableColumns,
resizableStyle,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The resizableStyle replaces style to make the cell API better predictable so that no unexpected attributes can be assigned from the outside.

isEditable,
isEditing,
isEditingDisabled,
Expand All @@ -101,6 +102,8 @@ export const TableTdElement = React.forwardRef<HTMLTableCellElement, TableTdElem
const Element = isRowHeader ? 'th' : 'td';
const isVisualRefresh = useVisualRefresh();

resizableStyle = resizableColumns ? {} : resizableStyle;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this condition looks counterintuitive. you apply resizableStyle only when resizableColumns is false. why is that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is taken as is from the current impl: https://github.com/cloudscape-design/components/blob/main/src/table/internal.tsx#L627

When resizable columns is on, the styles are assigned directly to cell nodes in src/table/column-widths-utils.ts and I think the properties like min-width and max-width are ignored.

I think there are further opportunities for refactoring and maybe this condition can be just removed but I'd not include this into the current PR.


nativeAttributes = { ...nativeAttributes, ...getTableCellRoleProps({ tableRole, isRowHeader, colIndex }) };

const stickyStyles = useStickyCellStyles({
Expand All @@ -115,10 +118,9 @@ export const TableTdElement = React.forwardRef<HTMLTableCellElement, TableTdElem

return (
<Element
style={{ ...style, ...stickyStyles.style }}
style={{ ...resizableStyle, ...stickyStyles.style }}
className={clsx(
styles['body-cell'],
wrapLines && styles['body-cell-wrap'],
isFirstRow && styles['body-cell-first-row'],
isLastRow && styles['body-cell-last-row'],
isSelected && styles['body-cell-selected'],
Expand All @@ -137,7 +139,6 @@ export const TableTdElement = React.forwardRef<HTMLTableCellElement, TableTdElem
hasSuccessIcon && styles['body-cell-has-success'],
level !== undefined && styles['body-cell-expandable'],
level !== undefined && styles[`expandable-level-${getLevelClassSuffix(level)}`],
verticalAlign === 'top' && styles['body-cell-align-top'],
stickyStyles.className
)}
onClick={onClick}
Expand All @@ -159,7 +160,15 @@ export const TableTdElement = React.forwardRef<HTMLTableCellElement, TableTdElem
</div>
)}

<div className={styles['body-cell-content']}>{children}</div>
<div
className={clsx(
styles['body-cell-content'],
verticalAlign === 'top' && styles['body-cell-align-top'],
wrapLines && styles['body-cell-wrap']
)}
>
{children}
</div>
</Element>
);
}
Expand Down
8 changes: 7 additions & 1 deletion src/table/column-widths-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ import { warnOnce } from '@cloudscape-design/component-toolkit/internal';

import { TableProps } from './interfaces';

export interface ColumnWidthStyle {
width?: string | number;
minWidth?: string | number;
maxWidth?: string | number;
}

export function checkColumnWidths(columnDefinitions: ReadonlyArray<TableProps.ColumnDefinition<any>>) {
for (const column of columnDefinitions) {
checkProperty(column, 'minWidth');
checkProperty(column, 'width');
}
}

export function setElementWidths(element: HTMLElement, styles: React.CSSProperties) {
export function setElementWidths(element: HTMLElement, styles: ColumnWidthStyle) {
function setProperty(property: 'width' | 'minWidth' | 'maxWidth') {
const value = styles[property];
let widthCssValue = '';
Expand Down
7 changes: 4 additions & 3 deletions src/table/header-cell/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useMergeRefs } from '../../internal/hooks/use-merge-refs';
import { useUniqueId } from '../../internal/hooks/use-unique-id';
import { KeyCode } from '../../internal/keycode';
import { GeneratedAnalyticsMetadataTableSort } from '../analytics-metadata/interfaces';
import { ColumnWidthStyle } from '../column-widths-utils';
import { TableProps } from '../interfaces';
import { Divider, Resizer } from '../resizer';
import { StickyColumnsModel } from '../sticky-columns';
Expand All @@ -24,7 +25,6 @@ import analyticsSelectors from '../analytics-metadata/styles.css.js';
import styles from './styles.css.js';

export interface TableHeaderCellProps<ItemType> {
style?: React.CSSProperties;
tabIndex: number;
column: TableProps.ColumnDefinition<ItemType>;
activeSortingColumn?: TableProps.SortingColumn<ItemType>;
Expand All @@ -40,6 +40,7 @@ export interface TableHeaderCellProps<ItemType> {
colIndex: number;
updateColumn: (columnId: PropertyKey, newWidth: number) => void;
resizableColumns?: boolean;
resizableStyle?: ColumnWidthStyle;
isEditable?: boolean;
columnId: PropertyKey;
stickyState: StickyColumnsModel;
Expand All @@ -53,7 +54,6 @@ export interface TableHeaderCellProps<ItemType> {
}

export function TableHeaderCell<ItemType>({
style,
tabIndex,
column,
activeSortingColumn,
Expand All @@ -69,6 +69,7 @@ export function TableHeaderCell<ItemType>({
colIndex,
updateColumn,
resizableColumns,
resizableStyle,
onResizeFinish,
isEditable,
columnId,
Expand Down Expand Up @@ -120,7 +121,7 @@ export function TableHeaderCell<ItemType>({

return (
<TableThElement
style={style}
resizableStyle={resizableStyle}
cellRef={cellRefCombined}
sortingStatus={sortingStatus}
sortingDisabled={sortingDisabled}
Expand Down
7 changes: 4 additions & 3 deletions src/table/header-cell/th-element.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { copyAnalyticsMetadataAttribute } from '@cloudscape-design/component-too
import { useSingleTabStopNavigation } from '../../internal/context/single-tab-stop-navigation-context';
import { useMergeRefs } from '../../internal/hooks/use-merge-refs';
import { useVisualRefresh } from '../../internal/hooks/use-visual-mode';
import { ColumnWidthStyle } from '../column-widths-utils';
import { TableProps } from '../interfaces';
import { StickyColumnsModel, useStickyCellStyles } from '../sticky-columns';
import { getTableColHeaderRoleProps, TableRole } from '../table-role';
Expand All @@ -18,7 +19,7 @@ import tableStyles from '../styles.css.js';
import styles from './styles.css.js';

export interface TableThElementProps {
style?: React.CSSProperties;
resizableStyle?: ColumnWidthStyle;
sortingStatus?: SortingStatus;
sortingDisabled?: boolean;
focusedComponent?: null | string;
Expand All @@ -37,7 +38,7 @@ export interface TableThElementProps {
}

export function TableThElement({
style,
resizableStyle,
sortingStatus,
sortingDisabled,
focusedComponent,
Expand Down Expand Up @@ -89,7 +90,7 @@ export function TableThElement({
},
stickyStyles.className
)}
style={{ ...style, ...stickyStyles.style }}
style={{ ...resizableStyle, ...stickyStyles.style }}
ref={mergedRef}
{...getTableColHeaderRoleProps({ tableRole, sortingStatus, colIndex })}
tabIndex={cellTabIndex === -1 ? undefined : cellTabIndex}
Expand Down
14 changes: 5 additions & 9 deletions src/table/internal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -621,15 +621,11 @@ const InternalTable = React.forwardRef(
<TableBodyCell
key={getColumnKey(column, colIndex)}
{...sharedCellProps}
style={
resizableColumns
? {}
: {
width: column.width,
minWidth: column.minWidth,
maxWidth: column.maxWidth,
}
}
resizableStyle={{
width: column.width,
minWidth: column.minWidth,
maxWidth: column.maxWidth,
}}
ariaLabels={ariaLabels}
column={column}
item={row.item}
Expand Down
1 change: 1 addition & 0 deletions src/table/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@

.table {
inline-size: 100%;
block-size: 100%;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change does not affect the table itself - the size of the table is determined by the size of its content.

However, the table element needs the height set for cells height to take an effect, see discussion: https://stackoverflow.com/questions/3215553/make-a-div-fill-an-entire-table-cell.

border-spacing: 0;
position: relative;
box-sizing: border-box;
Expand Down
2 changes: 1 addition & 1 deletion src/table/thead.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ const Thead = React.forwardRef(
<TableHeaderCell
{...commonCellProps}
key={columnId}
style={getColumnStyles(sticky, columnId)}
tabIndex={sticky ? -1 : 0}
focusedComponent={focusedComponent}
column={column}
Expand All @@ -137,6 +136,7 @@ const Thead = React.forwardRef(
updateColumn={updateColumn}
onResizeFinish={() => onResizeFinish(columnWidths)}
resizableColumns={resizableColumns}
resizableStyle={getColumnStyles(sticky, columnId)}
onClick={detail => {
setLastUserAction('sorting');
fireNonCancelableEvent(onSortingChange, detail);
Expand Down
11 changes: 4 additions & 7 deletions src/table/use-column-widths.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@ import React, { createContext, useContext, useEffect, useRef, useState } from 'r
import { useResizeObserver, useStableCallback } from '@cloudscape-design/component-toolkit/internal';
import { getLogicalBoundingClientRect } from '@cloudscape-design/component-toolkit/internal';

import { setElementWidths } from './column-widths-utils';
import { ColumnWidthStyle, setElementWidths } from './column-widths-utils';

export const DEFAULT_COLUMN_WIDTH = 120;

export interface ColumnWidthDefinition {
export interface ColumnWidthDefinition extends ColumnWidthStyle {
id: PropertyKey;
minWidth?: string | number;
maxWidth?: string | number;
width?: string | number;
}

function readWidths(
Expand Down Expand Up @@ -61,7 +58,7 @@ function updateWidths(
}

interface WidthsContext {
getColumnStyles(sticky: boolean, columnId: PropertyKey): React.CSSProperties;
getColumnStyles(sticky: boolean, columnId: PropertyKey): ColumnWidthStyle;
columnWidths: Map<PropertyKey, number>;
updateColumn: (columnId: PropertyKey, newWidth: number) => void;
setCell: (sticky: boolean, columnId: PropertyKey, node: null | HTMLElement) => void;
Expand Down Expand Up @@ -98,7 +95,7 @@ export function ColumnWidthsProvider({ visibleColumns, resizableColumns, contain
}
};

const getColumnStyles = (sticky: boolean, columnId: PropertyKey): React.CSSProperties => {
const getColumnStyles = (sticky: boolean, columnId: PropertyKey): ColumnWidthStyle => {
const column = visibleColumns.find(column => column.id === columnId);
if (!column) {
return {};
Expand Down
Loading