Skip to content

Commit 2bcc2f0

Browse files
authored
TS strict all components/types starting with letter 'A' (#3544)
* complete all components and shared types starting with 'A'
1 parent 770a8fb commit 2bcc2f0

File tree

11 files changed

+149
-121
lines changed

11 files changed

+149
-121
lines changed

packages/@react-spectrum/accordion/src/Accordion.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ interface AccordionItemProps<T> {
5454

5555
function AccordionItem<T>(props: AccordionItemProps<T>) {
5656
props = useProviderProps(props);
57-
let ref = useRef<HTMLButtonElement>();
57+
let ref = useRef<HTMLButtonElement>(null);
5858
let {state, item} = props;
5959
let {buttonProps, regionProps} = useAccordionItem<T>(props, state, ref);
6060
let isOpen = state.expandedKeys.has(item.key);

packages/@react-spectrum/accordion/stories/Accordion.stories.tsx

Lines changed: 44 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import {Story as _Story, Meta} from '@storybook/react';
1413
import {Accordion, Item} from '../src';
14+
import {ComponentMeta, ComponentStoryObj} from '@storybook/react';
1515
import React, {useState} from 'react';
1616
import {SpectrumAccordionProps} from '@react-types/accordion';
1717

@@ -20,53 +20,58 @@ type ItemType = {
2020
title: string
2121
};
2222

23-
/**
24-
* Helper type so `bind` returns the actual Story type.
25-
*/
26-
interface Story<T> extends _Story<T> {
27-
bind: (this: ThisParameterType<typeof Function.bind>, thisArg: Parameters<typeof Function.bind>[0], ...argArray: Parameters<typeof Function.bind>[1][]) => _Story<T>
28-
}
29-
3023
export default {
3124
title: 'Accordion',
32-
component: Accordion
33-
} as Meta<SpectrumAccordionProps<ItemType>>;
25+
component: Accordion,
26+
argTypes: {}
27+
} as ComponentMeta<typeof Accordion>;
28+
29+
export type AccordionStory = ComponentStoryObj<typeof Accordion>;
30+
31+
export const Default: AccordionStory = {
32+
args: {
33+
items: [
34+
{key: 'files', title: 'Your files'},
35+
{key: 'shared', title: 'Shared with you'},
36+
{key: 'last', title: 'Last item'}
37+
]
38+
},
39+
render: (args) => (
40+
<Accordion {...args}>
41+
{(item) => <Item key={(item as ItemType).key} title={(item as ItemType).title}>{(item as ItemType).key}</Item>}
42+
</Accordion>
43+
)
44+
};
45+
46+
export const DefaultExpandedKeys: AccordionStory = {
47+
args: {...Default.args, defaultExpandedKeys: ['files']},
48+
render: Default.render,
49+
name: 'defaultExpandedKeys: files'
50+
};
3451

35-
const AccordionRenderPropsTemplate: Story<SpectrumAccordionProps<ItemType>> = (args) => (
36-
<Accordion {...args}>
37-
{item => <Item key={item.key} title={item.title}>{item.key}</Item>}
38-
</Accordion>
39-
);
52+
export const DisabledKeys: AccordionStory = {
53+
args: {...Default.args, disabledKeys: ['files', 'shared']},
54+
render: Default.render,
55+
name: 'disabledKeys: files, shared'
56+
};
4057

41-
export const Default = AccordionRenderPropsTemplate.bind({});
42-
Default.storyName = 'default';
43-
Default.args = {
44-
items: [
45-
{key: 'files', title: 'Your files'},
46-
{key: 'shared', title: 'Shared with you'},
47-
{key: 'last', title: 'Last item'}
48-
]
58+
export const DisabledDefaultExpandedKeys: AccordionStory = {
59+
args: {...Default.args, defaultExpandedKeys: ['files'], disabledKeys: ['files', 'shared']},
60+
render: Default.render,
61+
name: 'defaultExpandedKeys: files, disabledKeys: files, shared'
4962
};
5063

51-
const AccordionTemplate: Story<SpectrumAccordionProps<ItemType>> = (args) => (
52-
<Accordion {...args} >
53-
<Item key="files" title="Your files">
54-
files
55-
</Item>
56-
<Item key="shared" title="Shared with you">
57-
shared
58-
</Item>
59-
<Item key="last" title="Last item">
60-
last
61-
</Item>
62-
</Accordion>
63-
);
64+
export const ControlledExpandedKeys: AccordionStory = {
65+
args: {...Default.args, defaultExpandedKeys: ['files']},
66+
render: (args) => <ControlledAccordion {...args} />,
67+
name: 'controlled ExpandedKeys'
68+
};
6469

6570

66-
const ControlledAccordionTemplate: Story<SpectrumAccordionProps<ItemType>> = (args) => {
71+
function ControlledAccordion<T>(props: SpectrumAccordionProps<T>) {
6772
let [openKeys, setOpenKeys] = useState<Set<React.Key>>(new Set(['files']));
6873
return (
69-
<Accordion {...args} expandedKeys={openKeys} onExpandedChange={setOpenKeys} >
74+
<Accordion {...props} expandedKeys={openKeys} onExpandedChange={setOpenKeys} >
7075
<Item key="files" title="Your files">
7176
files
7277
</Item>
@@ -78,23 +83,4 @@ const ControlledAccordionTemplate: Story<SpectrumAccordionProps<ItemType>> = (ar
7883
</Item>
7984
</Accordion>
8085
);
81-
};
82-
83-
export const DefaultExpandedKeys = AccordionTemplate.bind({});
84-
DefaultExpandedKeys.storyName = 'defaultExpandedKeys: files';
85-
DefaultExpandedKeys.args = {defaultExpandedKeys: ['files']};
86-
87-
export const ControlledExpandedKeys = ControlledAccordionTemplate.bind({});
88-
ControlledExpandedKeys.storyName = 'controlled ExpandedKeys';
89-
ControlledExpandedKeys.args = {};
90-
91-
export const DisabledKeys = AccordionTemplate.bind({});
92-
DisabledKeys.storyName = 'disabledKeys: files, shared';
93-
DisabledKeys.args = {disabledKeys: ['files', 'shared']};
94-
95-
export const DisabledDefaultExpandedKeys = AccordionTemplate.bind({});
96-
DisabledDefaultExpandedKeys.storyName = 'defaultExpandedKeys: files, disabledKeys: files, shared';
97-
DisabledDefaultExpandedKeys.args = {
98-
defaultExpandedKeys: ['files'],
99-
disabledKeys: ['files', 'shared']
100-
};
86+
}

packages/@react-spectrum/autocomplete/src/MobileSearchAutocomplete.tsx

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,15 @@ import {ListBoxBase, useListBoxLayout} from '@react-spectrum/listbox';
2727
import Magnifier from '@spectrum-icons/ui/Magnifier';
2828
import {mergeProps, useId} from '@react-aria/utils';
2929
import {ProgressCircle} from '@react-spectrum/progress';
30-
import React, {HTMLAttributes, ReactElement, ReactNode, RefObject, useCallback, useEffect, useRef, useState} from 'react';
30+
import React, {
31+
HTMLAttributes,
32+
ReactElement,
33+
ReactNode,
34+
useCallback,
35+
useEffect,
36+
useRef,
37+
useState
38+
} from 'react';
3139
import searchAutocompleteStyles from './searchautocomplete.css';
3240
import searchStyles from '@adobe/spectrum-css-temp/components/search/vars.css';
3341
import {setInteractionModality, useHover} from '@react-aria/interactions';
@@ -45,7 +53,7 @@ import {useOverlayTrigger} from '@react-aria/overlays';
4553
import {useProviderProps} from '@react-spectrum/provider';
4654
import {useSearchAutocomplete} from '@react-aria/autocomplete';
4755

48-
export const MobileSearchAutocomplete = React.forwardRef(function MobileSearchAutocomplete<T extends object>(props: SpectrumSearchAutocompleteProps<T>, ref: FocusableRef<HTMLElement>) {
56+
function _MobileSearchAutocomplete<T extends object>(props: SpectrumSearchAutocompleteProps<T>, ref: FocusableRef<HTMLElement>) {
4957
props = useProviderProps(props);
5058

5159
let {
@@ -71,7 +79,7 @@ export const MobileSearchAutocomplete = React.forwardRef(function MobileSearchAu
7179
defaultSelectedKey: undefined
7280
});
7381

74-
let buttonRef = useRef<HTMLElement>();
82+
let buttonRef = useRef<HTMLDivElement>(null);
7583
let domRef = useFocusableRef(ref, buttonRef);
7684
let {triggerProps, overlayProps} = useOverlayTrigger({type: 'listbox'}, state, buttonRef);
7785

@@ -82,7 +90,7 @@ export const MobileSearchAutocomplete = React.forwardRef(function MobileSearchAu
8290

8391
// Focus the button and show focus ring when clicking on the label
8492
labelProps.onClick = () => {
85-
if (!props.isDisabled) {
93+
if (!props.isDisabled && buttonRef.current) {
8694
buttonRef.current.focus();
8795
setInteractionModality('keyboard');
8896
}
@@ -119,10 +127,13 @@ export const MobileSearchAutocomplete = React.forwardRef(function MobileSearchAu
119127
</Tray>
120128
</>
121129
);
122-
});
130+
}
131+
132+
export let MobileSearchAutocomplete = React.forwardRef(_MobileSearchAutocomplete) as <T>(props: SpectrumSearchAutocompleteProps<T> & {ref?: FocusableRef<HTMLElement>}) => ReactElement;
133+
123134

124135
interface SearchAutocompleteButtonProps extends AriaButtonProps {
125-
icon?: ReactElement,
136+
icon?: ReactElement | null,
126137
isQuiet?: boolean,
127138
isDisabled?: boolean,
128139
isReadOnly?: boolean,
@@ -135,7 +146,9 @@ interface SearchAutocompleteButtonProps extends AriaButtonProps {
135146
className?: string
136147
}
137148

138-
const SearchAutocompleteButton = React.forwardRef(function SearchAutocompleteButton(props: SearchAutocompleteButtonProps, ref: RefObject<HTMLElement>) {
149+
// any type is because we don't want to call useObjectRef because this is an internal component and we know
150+
// we are always passing an object ref
151+
const SearchAutocompleteButton = React.forwardRef(function SearchAutocompleteButton(props: SearchAutocompleteButtonProps, ref: any) {
139152
let searchIcon = (
140153
<Magnifier data-testid="searchicon" />
141154
);
@@ -173,8 +186,8 @@ const SearchAutocompleteButton = React.forwardRef(function SearchAutocompleteBut
173186
let clearButton = (
174187
<ClearButton
175188
onPress={(e) => {
176-
clearInput();
177-
props.onPress(e);
189+
clearInput?.();
190+
props?.onPress?.(e);
178191
}}
179192
preventFocus
180193
aria-label={stringFormatter.format('clear')}
@@ -188,7 +201,6 @@ const SearchAutocompleteButton = React.forwardRef(function SearchAutocompleteBut
188201
isDisabled={isDisabled} />
189202
);
190203

191-
192204
let validation = React.cloneElement(validationIcon, {
193205
UNSAFE_className: classNames(
194206
textfieldStyles,
@@ -217,7 +229,7 @@ const SearchAutocompleteButton = React.forwardRef(function SearchAutocompleteBut
217229
<div
218230
{...mergeProps(hoverProps, focusProps, buttonProps)}
219231
aria-haspopup="dialog"
220-
ref={ref as RefObject<HTMLDivElement>}
232+
ref={ref}
221233
style={{...style, outline: 'none'}}
222234
className={
223235
classNames(
@@ -303,14 +315,14 @@ const SearchAutocompleteButton = React.forwardRef(function SearchAutocompleteBut
303315
);
304316
});
305317

306-
interface SearchAutocompleteTrayProps extends SpectrumSearchAutocompleteProps<unknown> {
307-
state: ComboBoxState<unknown>,
318+
interface SearchAutocompleteTrayProps<T> extends SpectrumSearchAutocompleteProps<T> {
319+
state: ComboBoxState<T>,
308320
overlayProps: HTMLAttributes<HTMLElement>,
309321
loadingIndicator?: ReactElement,
310322
onClose: () => void
311323
}
312324

313-
function SearchAutocompleteTray(props: SearchAutocompleteTrayProps) {
325+
function SearchAutocompleteTray<T>(props: SearchAutocompleteTrayProps<T>) {
314326
let searchIcon = (
315327
<Magnifier data-testid="searchicon" />
316328
);
@@ -329,15 +341,15 @@ function SearchAutocompleteTray(props: SearchAutocompleteTrayProps) {
329341
onSubmit
330342
} = props;
331343

332-
let timeout = useRef(null);
344+
let timeout = useRef<ReturnType<typeof setTimeout> | null>(null);
333345
let [showLoading, setShowLoading] = useState(false);
334-
let inputRef = useRef<HTMLInputElement>();
335-
let popoverRef = useRef<HTMLDivElement>();
336-
let listBoxRef = useRef<HTMLDivElement>();
346+
let inputRef = useRef<HTMLInputElement>(null);
347+
let popoverRef = useRef<HTMLDivElement>(null);
348+
let listBoxRef = useRef<HTMLDivElement>(null);
337349
let layout = useListBoxLayout(state);
338350
let stringFormatter = useLocalizedStringFormatter(intlMessages);
339351

340-
let {inputProps, listBoxProps, labelProps, clearButtonProps} = useSearchAutocomplete(
352+
let {inputProps, listBoxProps, labelProps, clearButtonProps} = useSearchAutocomplete<T>(
341353
{
342354
...props,
343355
keyboardDelegate: layout,
@@ -349,7 +361,9 @@ function SearchAutocompleteTray(props: SearchAutocompleteTrayProps) {
349361
);
350362

351363
React.useEffect(() => {
352-
focusSafely(inputRef.current);
364+
if (inputRef.current) {
365+
focusSafely(inputRef.current);
366+
}
353367

354368
// When the tray unmounts, set state.isFocused (i.e. the tray input's focus tracker) to false.
355369
// This is to prevent state.isFocused from being set to true when the tray closes via tapping on the underlay
@@ -421,7 +435,9 @@ function SearchAutocompleteTray(props: SearchAutocompleteTrayProps) {
421435
return;
422436
}
423437

424-
popoverRef.current.focus();
438+
if (popoverRef.current) {
439+
popoverRef.current.focus();
440+
}
425441
}, [inputRef, popoverRef, isTouchDown]);
426442

427443
let inputValue = inputProps.value;
@@ -444,8 +460,10 @@ function SearchAutocompleteTray(props: SearchAutocompleteTrayProps) {
444460
} else if (loadingState !== 'filtering') {
445461
// If loading is no longer happening, clear any timers and hide the loading circle
446462
setShowLoading(false);
447-
clearTimeout(timeout.current);
448-
timeout.current = null;
463+
if (timeout.current !== null) {
464+
clearTimeout(timeout.current);
465+
timeout.current = null;
466+
}
449467
}
450468

451469
lastInputValue.current = inputValue;
@@ -454,11 +472,17 @@ function SearchAutocompleteTray(props: SearchAutocompleteTrayProps) {
454472
let onKeyDown = (e) => {
455473
// Close virtual keyboard, close tray, and fire onSubmit if user hits Enter w/o any focused options
456474
if (e.key === 'Enter' && state.selectionManager.focusedKey == null) {
457-
popoverRef.current.focus();
458-
onClose();
459-
onSubmit(inputValue.toString(), null);
475+
popoverRef.current?.focus();
476+
if (onClose) {
477+
onClose();
478+
}
479+
if (onSubmit) {
480+
onSubmit(inputValue == null ? null : inputValue.toString(), null);
481+
}
460482
} else {
461-
inputProps.onKeyDown(e);
483+
if (inputProps.onKeyDown) {
484+
inputProps.onKeyDown(e);
485+
}
462486
}
463487
};
464488

@@ -491,9 +515,9 @@ function SearchAutocompleteTray(props: SearchAutocompleteTrayProps) {
491515
inputRef={inputRef}
492516
isDisabled={isDisabled}
493517
isLoading={showLoading && loadingState === 'filtering'}
494-
loadingIndicator={loadingState != null && loadingCircle}
518+
loadingIndicator={loadingState != null ? loadingCircle : undefined}
495519
validationState={validationState}
496-
wrapperChildren={(state.inputValue !== '' || loadingState === 'filtering' || validationState != null) && !props.isReadOnly && clearButton}
520+
wrapperChildren={((state.inputValue !== '' || loadingState === 'filtering' || validationState != null) && !props.isReadOnly) ? clearButton : undefined}
497521
icon={icon}
498522
UNSAFE_className={
499523
classNames(

0 commit comments

Comments
 (0)