Skip to content

Commit 46be285

Browse files
authored
fix: colorwheel stuck and table checkbox not aligned (adobe#9862)
* fix: colorwheel stuck and table checkbox not aligned * Fix items visible behind checkboxes * add pending api parity * update migration * update language and codemod * add codemods for Menu as well * fix hidden focus ring
1 parent ac718c6 commit 46be285

File tree

12 files changed

+182
-35
lines changed

12 files changed

+182
-35
lines changed

packages/@react-spectrum/s2/src/ActionMenu.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {useSpectrumContextProps} from './useSpectrumContextProps';
2626

2727
export interface ActionMenuProps<T> extends
2828
Pick<MenuTriggerProps, 'isOpen' | 'defaultOpen' | 'onOpenChange' | 'align' | 'direction' | 'shouldFlip'>,
29-
Pick<MenuProps<T>, 'children' | 'items' | 'disabledKeys' | 'onAction'>,
29+
Pick<MenuProps<T>, 'children' | 'items' | 'disabledKeys' | 'onAction' | 'shouldCloseOnSelect'>,
3030
Pick<ActionButtonProps, 'isDisabled' | 'isQuiet' | 'autoFocus' | 'size'>,
3131
StyleProps, DOMProps, AriaLabelingProps {
3232
/**
@@ -72,7 +72,8 @@ export const ActionMenu = /*#__PURE__*/(forwardRef as forwardRefType)(function A
7272
items={props.items}
7373
disabledKeys={props.disabledKeys}
7474
onAction={props.onAction}
75-
size={props.menuSize}>
75+
size={props.menuSize}
76+
shouldCloseOnSelect={props.shouldCloseOnSelect}>
7677
{props.children}
7778
</Menu>
7879
</MenuTrigger>

packages/@react-spectrum/s2/src/TableView.tsx

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -867,16 +867,26 @@ const tableHeader = style({
867867
});
868868

869869
const selectAllCheckbox = style({
870-
marginStart: 16 // table-edge-to-content, same between mobile and desktop
871870
});
872871

873872
const selectAllCheckboxColumn = style({
874-
padding: 0,
873+
paddingStart: {
874+
default: 0,
875+
':has([slot="selection"])': 16
876+
},
877+
paddingEnd: {
878+
default: 0,
879+
':has(slot="selection")': 8
880+
},
881+
paddingY: 0,
875882
height: 'full',
876883
boxSizing: 'border-box',
877884
outlineStyle: 'none',
878885
position: 'relative',
886+
display: 'flex',
879887
alignContent: 'center',
888+
alignItems: 'center',
889+
justifyContent: 'start',
880890
borderColor: {
881891
default: 'gray-300',
882892
forcedColors: 'ButtonBorder'
@@ -1009,15 +1019,22 @@ const stickyCell = {
10091019
const checkboxCellStyle = style({
10101020
...commonCellStyles,
10111021
...stickyCell,
1022+
display: 'flex',
10121023
paddingStart: 16,
1024+
paddingEnd: 8,
10131025
alignContent: 'center',
1026+
alignItems: 'center',
1027+
justifyContent: 'start',
10141028
height: 'calc(100% - 1px)',
10151029
borderBottomWidth: 0,
10161030
backgroundColor: '--rowBackgroundColor'
10171031
});
10181032

10191033
const cellContent = style({
1020-
truncate: true,
1034+
truncate: {
1035+
default: true,
1036+
isSticky: false
1037+
},
10211038
whiteSpace: {
10221039
default: 'nowrap',
10231040
overflowMode: {
@@ -1031,7 +1048,10 @@ const cellContent = style({
10311048
end: 'end'
10321049
}
10331050
},
1034-
width: 'full',
1051+
width: {
1052+
default: 'full',
1053+
':has([slot="selection"])': 'unset'
1054+
},
10351055
isolation: 'isolate',
10361056
padding: {
10371057
default: 4,
@@ -1058,7 +1078,14 @@ export interface CellProps extends Omit<RACCellProps, 'style' | 'className' | 'r
10581078
* A cell within a table row.
10591079
*/
10601080
export const Cell = forwardRef(function Cell(props: CellProps, ref: DOMRef<HTMLDivElement>) {
1061-
let {children, isSticky, showDivider = false, align, textValue, ...otherProps} = props;
1081+
let {
1082+
children,
1083+
isSticky,
1084+
showDivider = false,
1085+
align,
1086+
textValue,
1087+
...otherProps
1088+
} = props;
10621089
let domRef = useDOMRef(ref);
10631090
let tableVisualOptions = useContext(InternalTableContext);
10641091
textValue ||= typeof children === 'string' ? children : undefined;
@@ -1079,7 +1106,7 @@ export const Cell = forwardRef(function Cell(props: CellProps, ref: DOMRef<HTMLD
10791106
{...otherProps}>
10801107
{({id, isFocusVisible, hasChildItems, isTreeColumn, isExpanded, isDisabled}) => (
10811108
<>
1082-
{hasChildItems && isTreeColumn &&
1109+
{hasChildItems && isTreeColumn &&
10831110
<ExpandableRowChevron key={id} isDisabled={isDisabled} isExpanded={isExpanded} />
10841111
}
10851112
<span className={cellContent({...tableVisualOptions, isSticky, align: align || 'start'})}>{children}</span>
@@ -1252,7 +1279,7 @@ export const EditableCell = forwardRef(function EditableCell(props: EditableCell
12521279
{...otherProps}>
12531280
{({id, isFocusVisible, hasChildItems, isTreeColumn, isExpanded, isDisabled}) => (
12541281
<>
1255-
{hasChildItems && isTreeColumn &&
1282+
{hasChildItems && isTreeColumn &&
12561283
<ExpandableRowChevron key={id} isDisabled={isDisabled} isExpanded={isExpanded} />
12571284
}
12581285
<EditableCellInner {...props} isFocusVisible={isFocusVisible} cellRef={domRef as RefObject<HTMLDivElement>} />

packages/@react-spectrum/s2/style/spectrum-theme.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,12 +1112,11 @@ export const style = createTheme({
11121112
animationDuration: 150,
11131113
animationTimingFunction: 'default'
11141114
}),
1115-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
1116-
truncate: (_value: true) => ({
1117-
overflowX: 'hidden',
1118-
overflowY: 'hidden',
1119-
textOverflow: 'ellipsis',
1120-
whiteSpace: 'nowrap'
1115+
truncate: (value: boolean) => ({
1116+
overflowX: value ? 'hidden' : 'visible',
1117+
overflowY: value ? 'hidden' : 'visible',
1118+
textOverflow: value ? 'ellipsis' : 'clip',
1119+
whiteSpace: value ? 'nowrap' : 'normal'
11211120
}),
11221121
font: (value: keyof typeof fontSize) => {
11231122
let type = value.split('-')[0];

packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/actionmenu.test.ts.snap

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ let actionMenuItems = [
1616
</div>"
1717
`;
1818

19+
exports[`Renames closeOnSelect to shouldCloseOnSelect 1`] = `
20+
"import { MenuItem, ActionMenu } from "@react-spectrum/s2";
21+
22+
<div>
23+
<ActionMenu shouldCloseOnSelect>
24+
<MenuItem>Cut</MenuItem>
25+
<MenuItem>Copy</MenuItem>
26+
<MenuItem>Paste</MenuItem>
27+
</ActionMenu>
28+
</div>"
29+
`;
30+
1931
exports[`Static - no change 1`] = `
2032
"import { MenuItem, ActionMenu } from "@react-spectrum/s2";
2133

packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/menu.test.ts.snap

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,19 @@ exports[`Renames ContextualHelpTrigger to UnavailableMenuItemTrigger and Dialog
179179
</MenuTrigger>"
180180
`;
181181

182+
exports[`Renames closeOnSelect to shouldCloseOnSelect and moves to Menu 1`] = `
183+
"import { MenuItem, Menu, MenuTrigger, Button } from "@react-spectrum/s2";
184+
185+
<MenuTrigger>
186+
<Button>Edit</Button>
187+
<Menu shouldCloseOnSelect>
188+
<MenuItem>Cut</MenuItem>
189+
<MenuItem>Copy</MenuItem>
190+
<MenuItem>Paste</MenuItem>
191+
</Menu>
192+
</MenuTrigger>"
193+
`;
194+
182195
exports[`Static - Renames Item to MenuItem, Section to MenuSection 1`] = `
183196
"import {
184197
MenuItem,

packages/dev/codemods/src/s1-to-s2/__tests__/actionmenu.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,15 @@ let actionMenuItems = [
3333
</ActionMenu>
3434
</div>
3535
`);
36+
37+
test('Renames closeOnSelect to shouldCloseOnSelect', `
38+
import {ActionMenu, Item} from '@adobe/react-spectrum';
39+
40+
<div>
41+
<ActionMenu closeOnSelect>
42+
<Item>Cut</Item>
43+
<Item>Copy</Item>
44+
<Item>Paste</Item>
45+
</ActionMenu>
46+
</div>
47+
`);

packages/dev/codemods/src/s1-to-s2/__tests__/menu.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,16 @@ import {Menu, MenuTrigger, ContextualHelpTrigger, Item, Button, Dialog, Heading,
217217
</Menu>
218218
</MenuTrigger>
219219
`);
220+
221+
test('Renames closeOnSelect to shouldCloseOnSelect and moves to Menu', `
222+
import {Menu, MenuTrigger, Item, Button} from '@adobe/react-spectrum';
223+
224+
<MenuTrigger closeOnSelect>
225+
<Button>Edit</Button>
226+
<Menu>
227+
<Item>Cut</Item>
228+
<Item>Copy</Item>
229+
<Item>Paste</Item>
230+
</Menu>
231+
</MenuTrigger>
232+
`);

packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionMenu/transform.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import {commentOutProp} from '../../shared/transforms';
1+
import {commentOutProp, updatePropName} from '../../shared/transforms';
22
import {NodePath} from '@babel/traverse';
33
import * as t from '@babel/types';
44

55
/**
66
* Transforms ActionMenu:
7-
* - Comment out closeOnSelect (it has not been implemented yet).
7+
* - Rename `closeOnSelect` to `shouldCloseOnSelect`.
88
* - Comment out trigger (it has not been implemented yet).
99
*/
1010
export default function transformActionMenu(path: NodePath<t.JSXElement>): void {
11-
// Comment out closeOnSelect
12-
commentOutProp(path, {propName: 'closeOnSelect'});
11+
// Rename `closeOnSelect` to `shouldCloseOnSelect`
12+
updatePropName(path, {oldPropName: 'closeOnSelect', newPropName: 'shouldCloseOnSelect'});
1313

1414
// Comment out trigger
1515
commentOutProp(path, {propName: 'trigger'});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {movePropToChildComponent, updatePropName} from '../../shared/transforms';
2+
import {NodePath} from '@babel/traverse';
3+
import * as t from '@babel/types';
4+
5+
/**
6+
* Transforms MenuTrigger:
7+
* - Rename `closeOnSelect` to `shouldCloseOnSelect` and move it to the child `Menu`.
8+
*/
9+
export default function transformMenuTrigger(path: NodePath<t.JSXElement>): void {
10+
updatePropName(path, {oldPropName: 'closeOnSelect', newPropName: 'shouldCloseOnSelect'});
11+
12+
movePropToChildComponent(path, {
13+
parentComponentName: 'MenuTrigger',
14+
childComponentName: 'Menu',
15+
propName: 'shouldCloseOnSelect'
16+
});
17+
}

packages/dev/codemods/src/s1-to-s2/src/codemods/shared/transforms.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,57 @@ export function movePropToParentComponent(
595595
});
596596
}
597597

598+
/**
599+
* Moves a prop from the parent component onto a direct child JSX element.
600+
*
601+
* Example:
602+
* - MenuTrigger: Remove `shouldCloseOnSelect` and add it to the child `Menu` instead.
603+
*/
604+
export function movePropToChildComponent(
605+
path: NodePath<t.JSXElement>,
606+
options: {
607+
parentComponentName: string,
608+
childComponentName: string,
609+
propName: string
610+
}
611+
): void {
612+
const {parentComponentName, childComponentName, propName} = options;
613+
614+
if (
615+
!t.isJSXIdentifier(path.node.openingElement.name) ||
616+
getName(path, path.node.openingElement.name) !== parentComponentName
617+
) {
618+
return;
619+
}
620+
621+
let attrs = path.node.openingElement.attributes;
622+
let propAttr = attrs.find(
623+
(attr): attr is t.JSXAttribute => t.isJSXAttribute(attr) && attr.name.name === propName
624+
);
625+
if (!propAttr) {
626+
return;
627+
}
628+
629+
let childPath = path.get('children').find(
630+
(child) =>
631+
child.isJSXElement() &&
632+
t.isJSXIdentifier(child.node.openingElement.name) &&
633+
getName(path, child.node.openingElement.name) === childComponentName
634+
);
635+
636+
if (!childPath?.isJSXElement()) {
637+
return;
638+
}
639+
640+
childPath.node.openingElement.attributes.push(
641+
t.jsxAttribute(t.jsxIdentifier(propName), propAttr.value)
642+
);
643+
let index = attrs.indexOf(propAttr);
644+
if (index !== -1) {
645+
attrs.splice(index, 1);
646+
}
647+
}
648+
598649
/**
599650
* Update to use a new component.
600651
*

0 commit comments

Comments
 (0)