Skip to content

Commit

Permalink
Merge branch 'aria_pattern_utils' into reusable-tests
Browse files Browse the repository at this point in the history
# Conflicts:
#	packages/@react-aria/test-utils/src/menu.ts
#	packages/@react-spectrum/menu/test/MenuTrigger.test.js
#	packages/react-aria-components/test/Menu.test.tsx
  • Loading branch information
snowystinger committed Sep 20, 2024
2 parents d087bfd + 2d1067c commit c509656
Show file tree
Hide file tree
Showing 133 changed files with 4,391 additions and 1,044 deletions.
27 changes: 23 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -482,12 +482,17 @@ jobs:
steps:
- attach_workspace:
at: /tmp/dist

- run:
name: Install AzCopy
command: |
wget -O azcopy.tar.gz https://aka.ms/downloadazcopy-v10-linux
tar -xf azcopy.tar.gz
mv ./azcopy_linux_amd64_*/azcopy /usr/local/bin/
- run:
name: deploy
command: |
if [ $AZURE_STORAGE_SAS_TOKEN ]; then
az storage blob upload-batch -d reactspectrum -s /tmp/dist --account-name reactspectrum
azcopy copy "/tmp/dist/*" "https://reactspectrum.blob.core.windows.net/reactspectrum${AZURE_STORAGE_SAS_TOKEN}" --recursive
fi
# Separate deploy workflow for the test docs built w/ verdaccio packages so it doesn't hold up the other deploy workflows
Expand All @@ -497,11 +502,19 @@ jobs:
steps:
- attach_workspace:
at: /tmp/verdaccio_dist
- run:
name: Install AzCopy
command: |
wget -O azcopy.tar.gz https://aka.ms/downloadazcopy-v10-linux
tar -xf azcopy.tar.gz
mv ./azcopy_linux_amd64_*/azcopy /usr/local/bin/
- run:
name: deploy
command: |
if [ $AZURE_STORAGE_SAS_TOKEN ]; then
az storage blob upload-batch -d reactspectrum/$CIRCLE_SHA1/verdaccio -s /tmp/verdaccio_dist/*/verdaccio --account-name reactspectrum
for dir in /tmp/verdaccio_dist/*/verdaccio; do
azcopy copy "$dir/*" "https://reactspectrum.blob.core.windows.net/reactspectrum/$CIRCLE_SHA1/verdaccio${AZURE_STORAGE_SAS_TOKEN}" --recursive
done
fi
deploy-production:
Expand All @@ -510,9 +523,15 @@ jobs:
steps:
- attach_workspace:
at: /tmp/dist
- run:
name: Install AzCopy
command: |
wget -O azcopy.tar.gz https://aka.ms/downloadazcopy-v10-linux
tar -xf azcopy.tar.gz
mv ./azcopy_linux_amd64_*/azcopy /usr/local/bin/
- run:
name: deploy
command: az storage blob upload-batch -d "\$web" -s /tmp/dist/production/docs --account-name reactspectrum --overwrite true
command: azcopy copy "/tmp/dist/production/docs/*" "https://reactspectrum.blob.core.windows.net/\$web${AZURE_STORAGE_SAS_TOKEN}" --recursive

comment:
executor: rsp
Expand Down
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ module.exports = {
'AsyncIterable': 'readonly',
'FileSystemFileEntry': 'readonly',
'FileSystemDirectoryEntry': 'readonly',
'FileSystemEntry': 'readonly'
'FileSystemEntry': 'readonly',
'IS_REACT_ACT_ENVIRONMENT': 'readonly'
},
settings: {
jsdoc: {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@
"@parcel/transformer-css": {
"cssModules": {
"global": true,
"pattern": "[content-hash]_[local]",
"exclude": [
"**/*.global.css",
"packages/@react-aria/example-theme/**",
Expand Down
1 change: 1 addition & 0 deletions packages/@internationalized/date/src/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ export function startOfWeek(date: DateValue, locale: string): DateValue {
export function endOfWeek(date: ZonedDateTime, locale: string): ZonedDateTime;
export function endOfWeek(date: CalendarDateTime, locale: string): CalendarDateTime;
export function endOfWeek(date: CalendarDate, locale: string): CalendarDate;
export function endOfWeek(date: DateValue, locale: string): DateValue;
export function endOfWeek(date: DateValue, locale: string): DateValue {
return startOfWeek(date, locale).add({days: 6});
}
Expand Down
2 changes: 1 addition & 1 deletion packages/@react-aria/datepicker/src/useDateSegment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
// Otherwise, when tapping on a segment in Android Chrome and then entering text,
// composition events will be fired that break the DOM structure and crash the page.
let selection = window.getSelection();
if (ref.current.contains(selection.anchorNode)) {
if (ref.current && ref.current.contains(selection.anchorNode)) {
selection.collapse(ref.current);
}
});
Expand Down
5 changes: 4 additions & 1 deletion packages/@react-aria/dnd/src/DragManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,10 @@ class DragSession {
// Announce first drop target after drag start announcement finishes.
// Otherwise, it will never get announced because drag start announcement is assertive.
if (!this.initialFocused) {
announce(item?.element.getAttribute('aria-label'), 'polite');
let label = item?.element.getAttribute('aria-label');
if (label) {
announce(label, 'polite');
}
this.initialFocused = true;
}
}
Expand Down
41 changes: 34 additions & 7 deletions packages/@react-aria/interactions/src/usePress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ export function usePress(props: PressHookProps): PressResult {

// Due to browser inconsistencies, especially on mobile browsers, we prevent
// default on pointer down and handle focusing the pressable element ourselves.
if (shouldPreventDefault(e.currentTarget as Element)) {
if (shouldPreventDefaultDown(e.currentTarget as Element)) {
e.preventDefault();
}

Expand Down Expand Up @@ -452,7 +452,7 @@ export function usePress(props: PressHookProps): PressResult {
// Chrome and Firefox on touch Windows devices require mouse down events
// to be canceled in addition to pointer events, or an extra asynchronous
// focus event will be fired.
if (shouldPreventDefault(e.currentTarget as Element)) {
if (shouldPreventDefaultDown(e.currentTarget as Element)) {
e.preventDefault();
}

Expand Down Expand Up @@ -510,6 +510,25 @@ export function usePress(props: PressHookProps): PressResult {
if (!allowTextSelectionOnPress) {
restoreTextSelection(state.target);
}

// Prevent subsequent touchend event from triggering onClick on unrelated elements on Android. See below.
// Both 'touch' and 'pen' pointerTypes trigger onTouchEnd, but 'mouse' does not.
if ('ontouchend' in state.target && e.pointerType !== 'mouse') {
addGlobalListener(state.target, 'touchend', onTouchEnd, {once: true});
}
}
};

// This is a workaround for an Android Chrome/Firefox issue where click events are fired on an incorrect element
// if the original target is removed during onPointerUp (before onClick).
// https://github.com/adobe/react-spectrum/issues/1513
// https://issues.chromium.org/issues/40732224
// Note: this event must be registered directly on the element, not via React props in order to work.
// https://github.com/facebook/react/issues/9809
let onTouchEnd = (e: TouchEvent) => {
// Don't preventDefault if we actually want the default (e.g. submit/link click).
if (shouldPreventDefaultUp(e.target as Element)) {
e.preventDefault();
}
};

Expand All @@ -534,7 +553,7 @@ export function usePress(props: PressHookProps): PressResult {

// Due to browser inconsistencies, especially on mobile browsers, we prevent
// default on mouse down and handle focusing the pressable element ourselves.
if (shouldPreventDefault(e.currentTarget)) {
if (shouldPreventDefaultDown(e.currentTarget)) {
e.preventDefault();
}

Expand Down Expand Up @@ -914,16 +933,16 @@ function isOverTarget(point: EventPoint, target: Element) {
return areRectanglesOverlapping(rect, pointRect);
}

function shouldPreventDefault(target: Element) {
function shouldPreventDefaultDown(target: Element) {
// We cannot prevent default if the target is a draggable element.
return !(target instanceof HTMLElement) || !target.hasAttribute('draggable');
}

function shouldPreventDefaultKeyboard(target: Element, key: string) {
function shouldPreventDefaultUp(target: Element) {
if (target instanceof HTMLInputElement) {
return !isValidInputKey(target, key);
return false;
}

if (target instanceof HTMLButtonElement) {
return target.type !== 'submit' && target.type !== 'reset';
}
Expand All @@ -935,6 +954,14 @@ function shouldPreventDefaultKeyboard(target: Element, key: string) {
return true;
}

function shouldPreventDefaultKeyboard(target: Element, key: string) {
if (target instanceof HTMLInputElement) {
return !isValidInputKey(target, key);
}

return shouldPreventDefaultUp(target);
}

const nonTextInputTypes = new Set([
'checkbox',
'radio',
Expand Down
76 changes: 76 additions & 0 deletions packages/@react-aria/interactions/stories/usePress-stories.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2024 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
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

.outer-div {
background-color: #333;
width: 320px;
height: 320px;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
margin-bottom: 1000px;
}

.open-btn {
background-color: blue;
font-size: 32px;
padding: 10px;
}

.visit-link {
color: #9999ff;
font-size: 16px;
}

.fake-modal {
background-color: rgba(224, 64, 0, 0.5);
position: absolute;
width: 320px;
height: 320px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
}

.side-by-side {
color: white;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}

.my-btn {
background-color: rgb(0, 128, 0);
cursor: pointer;
padding: 5px;
}

.fake-modal h1 {
color: white;
font-size: 60px;
margin: 0;
padding: 0;
}

.fake-modal .close-btn {
background-color: red;
font-size: 16px;
padding: 10px;
}

.OnPress {
cursor: pointer;
color: #ffffff;
}
86 changes: 86 additions & 0 deletions packages/@react-aria/interactions/stories/usePress.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright 2024 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
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import React from 'react';
import styles from './usePress-stories.css';
import {usePress} from '@react-aria/interactions';

export default {
title: 'usePress'
};

export function TouchIssue() {
const [opened, setOpened] = React.useState(false);
const handleOpen = React.useCallback(() => {
console.log('opening');
setOpened(true);
}, []);
const handleClose = React.useCallback(() => {
console.log('closing');
setOpened(false);
}, []);
const handleOnClick = React.useCallback(() => {
alert('clicked it');
}, []);

return (
<div className={styles['outer-div']}>
<OnPress onPress={handleOpen} className={styles['open-btn']}>
Open
</OnPress>
<div className={styles['side-by-side']}>
<div>Some text</div>
<a href="https://www.google.com" className={styles['visit-link']}>
Another Link
</a>
<button className={styles['my-btn']} onClick={handleOnClick}>
On Click
</button>
</div>

{opened && (
<div className={styles['fake-modal']}>
<h1>Header</h1>
<div className={styles['side-by-side']}>
<OnPress onPress={handleClose} className={styles['close-btn']}>
Close 1
</OnPress>
<OnPress onPress={handleClose} className={styles['close-btn']}>
Close 2
</OnPress>
<OnPress onPress={handleClose} className={styles['close-btn']}>
Close 3
</OnPress>
</div>
</div>
)}
</div>
);
}

function OnPress(props) {
const {className, onPress, children} = props;

const {pressProps} = usePress({
onPress
});

return (
<div
{...pressProps}
role="button"
tabIndex={0}
className={`OnPress ${className || ''}`}>
{children}
</div>
);
}
Loading

0 comments on commit c509656

Please sign in to comment.