Skip to content

Commit d88e834

Browse files
committed
a11y and test edits
1 parent 6a6e433 commit d88e834

File tree

13 files changed

+398
-310
lines changed

13 files changed

+398
-310
lines changed

apps/docs/docs/components/other/Calendar/_mobileExamples.mdx

Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@ A basic Calendar with date selection functionality. The Calendar component is us
66
function Example() {
77
const [selectedDate, setSelectedDate] = useState(new Date());
88

9-
return (
10-
<Calendar
11-
selectedDate={selectedDate}
12-
onPressDate={setSelectedDate}
13-
/>
14-
);
9+
return <Calendar selectedDate={selectedDate} onPressDate={setSelectedDate} />;
1510
}
1611
```
1712

@@ -23,12 +18,7 @@ A Calendar without an initially selected date.
2318
function Example() {
2419
const [selectedDate, setSelectedDate] = useState(null);
2520

26-
return (
27-
<Calendar
28-
selectedDate={selectedDate}
29-
onPressDate={setSelectedDate}
30-
/>
31-
);
21+
return <Calendar selectedDate={selectedDate} onPressDate={setSelectedDate} />;
3222
}
3323
```
3424

@@ -39,17 +29,11 @@ The `seedDate` prop controls which month the Calendar opens to when there is no
3929
```jsx
4030
function Example() {
4131
const [selectedDate, setSelectedDate] = useState(null);
42-
32+
4333
const today = new Date(new Date().setHours(0, 0, 0, 0));
4434
const seedDate = new Date(today.getFullYear(), today.getMonth() + 1, 15);
4535

46-
return (
47-
<Calendar
48-
selectedDate={selectedDate}
49-
onPressDate={setSelectedDate}
50-
seedDate={seedDate}
51-
/>
52-
);
36+
return <Calendar selectedDate={selectedDate} onPressDate={setSelectedDate} seedDate={seedDate} />;
5337
}
5438
```
5539

@@ -84,7 +68,7 @@ Restrict selection to future dates by setting `minDate` to today.
8468
```jsx
8569
function Example() {
8670
const [selectedDate, setSelectedDate] = useState(null);
87-
71+
8872
const today = new Date(new Date().setHours(0, 0, 0, 0));
8973

9074
return (
@@ -105,7 +89,7 @@ Use `highlightedDates` to visually emphasize specific dates or date ranges. A nu
10589
```jsx
10690
function Example() {
10791
const [selectedDate, setSelectedDate] = useState(new Date());
108-
92+
10993
const today = new Date(new Date().setHours(0, 0, 0, 0));
11094
const yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1);
11195
const nextWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7);
@@ -127,30 +111,30 @@ Use `disabledDates` to prevent selection of specific dates or date ranges. Make
127111
```jsx
128112
function Example() {
129113
const [selectedDate, setSelectedDate] = useState(null);
130-
114+
131115
const today = new Date(new Date().setHours(0, 0, 0, 0));
132-
116+
133117
// Disable weekends for demonstration
134118
const getNextWeekendDates = (centerDate) => {
135119
const weekends = [];
136120
const currentDate = new Date(centerDate);
137-
121+
138122
// Find next 4 weekends
139123
for (let i = 0; i < 4; i++) {
140124
// Find next Saturday
141125
const daysUntilSaturday = (6 - currentDate.getDay() + 7) % 7 || 7;
142126
currentDate.setDate(currentDate.getDate() + daysUntilSaturday);
143-
127+
144128
const saturday = new Date(currentDate);
145129
const sunday = new Date(currentDate);
146130
sunday.setDate(sunday.getDate() + 1);
147-
131+
148132
weekends.push([saturday, sunday]);
149-
133+
150134
// Move to next week
151135
currentDate.setDate(currentDate.getDate() + 7);
152136
}
153-
137+
154138
return weekends;
155139
};
156140

@@ -172,7 +156,7 @@ Highlight a date range using a tuple `[startDate, endDate]`.
172156
```jsx
173157
function Example() {
174158
const [selectedDate, setSelectedDate] = useState(new Date());
175-
159+
176160
const today = new Date(new Date().setHours(0, 0, 0, 0));
177161
const yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1);
178162
const nextWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7);
@@ -194,7 +178,7 @@ Hide the navigation arrows with `hideControls`. This is typically used when `min
194178
```jsx
195179
function Example() {
196180
const [selectedDate, setSelectedDate] = useState(new Date());
197-
181+
198182
const today = new Date(new Date().setHours(0, 0, 0, 0));
199183
const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
200184
const lastDayOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0);
@@ -219,12 +203,7 @@ Disable the entire Calendar with the `disabled` prop.
219203
function Example() {
220204
const selectedDate = new Date();
221205

222-
return (
223-
<Calendar
224-
selectedDate={selectedDate}
225-
disabled
226-
/>
227-
);
206+
return <Calendar selectedDate={selectedDate} disabled />;
228207
}
229208
```
230209

@@ -235,7 +214,7 @@ Always provide accessibility labels for the navigation controls and error messag
235214
```jsx
236215
function Example() {
237216
const [selectedDate, setSelectedDate] = useState(new Date());
238-
217+
239218
const today = new Date(new Date().setHours(0, 0, 0, 0));
240219
const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, today.getDate());
241220

Lines changed: 94 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
import React, { memo, useCallback, useMemo } from 'react';
2-
import { ActivityIndicator, type PressableStateCallbackType, type ViewStyle } from 'react-native';
1+
import React, { forwardRef, memo, useCallback, useMemo } from 'react';
2+
import {
3+
ActivityIndicator,
4+
type PressableStateCallbackType,
5+
type View,
6+
type ViewStyle,
7+
} from 'react-native';
38
import { transparentVariants, variants } from '@coinbase/cds-common/tokens/button';
49
import { interactableHeight } from '@coinbase/cds-common/tokens/interactableHeight';
510
import type { IconButtonVariant, IconName, SharedProps } from '@coinbase/cds-common/types';
@@ -27,87 +32,99 @@ export type IconButtonBaseProps = SharedProps &
2732

2833
export type IconButtonProps = IconButtonBaseProps;
2934

30-
export const IconButton = memo(function IconButton({
31-
name,
32-
active,
33-
variant = 'secondary',
34-
transparent,
35-
compact = true,
36-
background,
37-
color,
38-
borderColor,
39-
borderWidth = 100,
40-
borderRadius = 1000,
41-
feedback = compact ? 'light' : 'normal',
42-
flush,
43-
loading,
44-
style,
45-
accessibilityHint,
46-
accessibilityLabel,
47-
...props
48-
}: IconButtonProps) {
49-
const theme = useTheme();
50-
const iconSize = compact ? 's' : 'm';
35+
export const IconButton = memo(
36+
forwardRef<View, IconButtonProps>(function IconButton(
37+
{
38+
name,
39+
active,
40+
variant = 'secondary',
41+
transparent,
42+
compact = true,
43+
background,
44+
color,
45+
borderColor,
46+
borderWidth = 100,
47+
borderRadius = 1000,
48+
feedback = compact ? 'light' : 'normal',
49+
flush,
50+
loading,
51+
style,
52+
accessibilityHint,
53+
accessibilityLabel,
54+
...props
55+
},
56+
ref,
57+
) {
58+
const theme = useTheme();
59+
const iconSize = compact ? 's' : 'm';
5160

52-
const variantMap = transparent ? transparentVariants : variants;
53-
const variantStyle = variantMap[variant];
61+
const variantMap = transparent ? transparentVariants : variants;
62+
const variantStyle = variantMap[variant];
5463

55-
const colorValue = color ?? variantStyle.color;
56-
const backgroundValue = background ?? variantStyle.background;
57-
const borderColorValue = borderColor ?? variantStyle.borderColor;
64+
const colorValue = color ?? variantStyle.color;
65+
const backgroundValue = background ?? variantStyle.background;
66+
const borderColorValue = borderColor ?? variantStyle.borderColor;
5867

59-
const minHeight = interactableHeight[compact ? 'compact' : 'regular'];
68+
const minHeight = interactableHeight[compact ? 'compact' : 'regular'];
6069

61-
const { marginStart, marginEnd } = getButtonSpacingProps({ compact, flush });
70+
const { marginStart, marginEnd } = getButtonSpacingProps({ compact, flush });
6271

63-
const sizingStyle = useMemo<ViewStyle>(
64-
() => ({
65-
height: minHeight,
66-
width: minHeight,
67-
alignItems: 'center',
68-
flexDirection: 'column',
69-
justifyContent: 'center',
70-
}),
71-
[minHeight],
72-
);
72+
const sizingStyle = useMemo<ViewStyle>(
73+
() => ({
74+
height: minHeight,
75+
width: minHeight,
76+
alignItems: 'center',
77+
flexDirection: 'column',
78+
justifyContent: 'center',
79+
}),
80+
[minHeight],
81+
);
7382

74-
const pressableStyle = useCallback(
75-
(state: PressableStateCallbackType) => [
76-
sizingStyle,
77-
typeof style === 'function' ? style(state) : style,
78-
],
79-
[sizingStyle, style],
80-
);
83+
const pressableStyle = useCallback(
84+
(state: PressableStateCallbackType) => [
85+
sizingStyle,
86+
typeof style === 'function' ? style(state) : style,
87+
],
88+
[sizingStyle, style],
89+
);
8190

82-
return (
83-
<Pressable
84-
accessibilityHint={accessibilityHint}
85-
accessibilityLabel={loading ? `${accessibilityLabel ?? ''}, loading` : accessibilityLabel}
86-
background={backgroundValue}
87-
borderColor={borderColorValue}
88-
borderRadius={borderRadius}
89-
borderWidth={borderWidth}
90-
feedback={feedback}
91-
loading={loading}
92-
marginEnd={marginEnd}
93-
marginStart={marginStart}
94-
style={pressableStyle}
95-
transparentWhileInactive={transparent}
96-
{...props}
97-
>
98-
{loading ? (
99-
<ActivityIndicator
100-
color={theme.color[colorValue]}
101-
size="small"
102-
style={sizingStyle}
103-
testID={props.testID ? `${props.testID}-activity-indicator` : undefined}
104-
/>
105-
) : (
106-
/* TO DO: test using currentColor like web does on Icon here */
107-
<Icon active={active} color={colorValue} name={name} size={iconSize} style={sizingStyle} />
108-
)}
109-
</Pressable>
110-
);
111-
});
91+
return (
92+
<Pressable
93+
ref={ref}
94+
accessibilityHint={accessibilityHint}
95+
accessibilityLabel={loading ? `${accessibilityLabel ?? ''}, loading` : accessibilityLabel}
96+
background={backgroundValue}
97+
borderColor={borderColorValue}
98+
borderRadius={borderRadius}
99+
borderWidth={borderWidth}
100+
feedback={feedback}
101+
loading={loading}
102+
marginEnd={marginEnd}
103+
marginStart={marginStart}
104+
style={pressableStyle}
105+
transparentWhileInactive={transparent}
106+
{...props}
107+
>
108+
{loading ? (
109+
<ActivityIndicator
110+
color={theme.color[colorValue]}
111+
size="small"
112+
style={sizingStyle}
113+
testID={props.testID ? `${props.testID}-activity-indicator` : undefined}
114+
/>
115+
) : (
116+
/* TO DO: test using currentColor like web does on Icon here */
117+
<Icon
118+
active={active}
119+
color={colorValue}
120+
name={name}
121+
size={iconSize}
122+
style={sizingStyle}
123+
/>
124+
)}
125+
</Pressable>
126+
);
127+
}),
128+
);
112129

113130
IconButton.displayName = 'IconButton';
Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import React, { memo, useContext } from 'react';
1+
import React, { forwardRef, memo, useContext } from 'react';
2+
import type { View } from 'react-native';
23
import type { IconButtonVariant, InputVariant } from '@coinbase/cds-common/types';
34

45
import { IconButton, type IconButtonProps } from '../buttons/IconButton';
@@ -23,26 +24,32 @@ export type InputIconButtonProps = IconButtonProps & {
2324
disableInheritFocusStyle?: boolean;
2425
};
2526

26-
export const InputIconButton = memo(function InputIconButton({
27-
disableInheritFocusStyle = false,
28-
testID,
29-
variant = 'primary',
30-
accessibilityLabel,
31-
accessibilityHint,
32-
...props
33-
}: InputIconButtonProps) {
34-
const contextVariant = useContext(TextInputFocusVariantContext);
35-
const transformedVariant = contextVariant ? variantTransformMap[contextVariant] : variant;
27+
export const InputIconButton = memo(
28+
forwardRef<View, InputIconButtonProps>(function InputIconButton(
29+
{
30+
disableInheritFocusStyle = false,
31+
testID,
32+
variant = 'primary',
33+
accessibilityLabel,
34+
accessibilityHint,
35+
...props
36+
},
37+
ref,
38+
) {
39+
const contextVariant = useContext(TextInputFocusVariantContext);
40+
const transformedVariant = contextVariant ? variantTransformMap[contextVariant] : variant;
3641

37-
return (
38-
<Box paddingEnd={0.5} paddingStart={1} testID={testID}>
39-
<IconButton
40-
transparent
41-
accessibilityHint={accessibilityHint ?? props.name}
42-
accessibilityLabel={accessibilityLabel ?? props.name}
43-
variant={disableInheritFocusStyle ? variant : transformedVariant}
44-
{...props}
45-
/>
46-
</Box>
47-
);
48-
});
42+
return (
43+
<Box paddingEnd={0.5} paddingStart={1} testID={testID}>
44+
<IconButton
45+
ref={ref}
46+
transparent
47+
accessibilityHint={accessibilityHint ?? props.name}
48+
accessibilityLabel={accessibilityLabel ?? props.name}
49+
variant={disableInheritFocusStyle ? variant : transformedVariant}
50+
{...props}
51+
/>
52+
</Box>
53+
);
54+
}),
55+
);

0 commit comments

Comments
 (0)