Skip to content

Commit 006dcb9

Browse files
Fix: Conflicting states for accessibility and non-accessibility users (#354)
* adding accessibilityState.disabled prop to Slider.js * removing java logic to trigger accessibility disabled announcement * jest tests accessiblityState.disabled and disabled prop * update jest snapshots * adding invariant check on disabled prop type Flow check on master seems to not work fabOnReact/react-native-notes#2 (comment) As an alternative I implemented an invariant check. https://github.com/fabriziobertoglio1987/react-native/blob/76a2cf3569571b943b4bcc6867e069338ff88f1f/Libraries/Lists/VirtualizedList.js#L1240-L1245 https://github.com/zertosh/invariant * adding Slider disabled example * trigger invariant error when props.disabled is null * remove check on prop type commit 16d321b I will try to fix .flowconfig and enable flow type checking for Slider props * improve test cases descriptions * add LogBox (console.warn) message to notify developer conflicting accessibility disabled state * update LogBox warning text * remove LogBox warning * jest fix obsolete snapshots * Add test for reverted disabled/enabled case There's already a test, where disabled={true} is checked against accessibilityState={{disabled: false}}, but to double check all the combinations this commit adds new test that acts as the inverted scenario to make sure that conflicts are avoided both directions. * avoid conflicting disabled states It would be better to show that both these properties can be used separately without fear of another. #354 (comment) Co-authored-by: BartoszKlonowski <[email protected]>
1 parent 92195c2 commit 006dcb9

File tree

5 files changed

+146
-14
lines changed

5 files changed

+146
-14
lines changed

example/SliderExample.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,4 +223,18 @@ export const examples = [
223223
return <SliderExample value={0.6} vertical />;
224224
},
225225
},
226+
{
227+
title: 'Disabled slider',
228+
platform: 'android',
229+
render(): Element<any> {
230+
return <SliderExample disabled value={0.6} />;
231+
},
232+
},
233+
{
234+
title: 'Slider with accessibilityState disabled',
235+
platform: 'android',
236+
render(): Element<any> {
237+
return <SliderExample disabled value={0.6} />;
238+
},
239+
},
226240
];

src/android/src/main/java/com/reactnativecommunity/slider/ReactSlider.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import android.graphics.drawable.BitmapDrawable;
1313
import android.os.Build;
1414
import android.util.AttributeSet;
15-
import android.view.MotionEvent;
1615
import android.view.accessibility.AccessibilityEvent;
1716
import android.view.accessibility.AccessibilityManager;
1817
import androidx.appcompat.widget.AppCompatSeekBar;
@@ -153,17 +152,6 @@ public void run() {
153152
}
154153
}
155154

156-
@Override
157-
public boolean onTouchEvent(MotionEvent arg0) {
158-
super.onTouchEvent(arg0);
159-
160-
if (arg0.getActionMasked() == MotionEvent.ACTION_DOWN && this.isEnabled() == false) {
161-
announceForAccessibility("slider disabled");
162-
}
163-
// Returns: True if the view handled the hover event
164-
return true;
165-
}
166-
167155
public void setupAccessibility(int index) {
168156
if (mAccessibilityUnits != null && mAccessibilityIncrements != null && mAccessibilityIncrements.size() - 1 == (int)mMaxValue) {
169157
String sliderValue = mAccessibilityIncrements.get(index);

src/js/Slider.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,16 @@ const SliderComponent = (
263263
}
264264
: null;
265265

266+
const _disabled =
267+
typeof props.disabled === 'boolean'
268+
? props.disabled
269+
: props.accessibilityState?.disabled === true;
270+
271+
const _accessibilityState =
272+
typeof props.disabled === 'boolean'
273+
? {...props.accessibilityState, disabled: props.disabled}
274+
: props.accessibilityState;
275+
266276
const onChangeEvent = onValueChangeEvent;
267277
const onSlidingStartEvent = onSlidingStart
268278
? (event: Event) => {
@@ -289,9 +299,11 @@ const SliderComponent = (
289299
onRNCSliderSlidingStart={onSlidingStartEvent}
290300
onRNCSliderSlidingComplete={onSlidingCompleteEvent}
291301
onRNCSliderValueChange={onValueChangeEvent}
292-
enabled={!props.disabled}
302+
enabled={!_disabled}
303+
disabled={_disabled}
293304
onStartShouldSetResponder={() => true}
294305
onResponderTerminationRequest={() => false}
306+
accessibilityState={_accessibilityState}
295307
/>
296308
);
297309
};
@@ -303,7 +315,6 @@ const SliderWithRef = React.forwardRef(SliderComponent);
303315
* and run Flow. */
304316

305317
SliderWithRef.defaultProps = {
306-
disabled: false,
307318
value: 0,
308319
minimumValue: 0,
309320
maximumValue: 1,

src/js/__tests__/Slider.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,30 @@ describe('<Slider />', () => {
1717
expect(tree).toMatchSnapshot();
1818
});
1919

20+
it('accessibilityState disabled sets disabled={true}', () => {
21+
const tree = renderer
22+
.create(<Slider accessibilityState={{disabled: true}} />)
23+
.toJSON();
24+
25+
expect(tree).toMatchSnapshot();
26+
});
27+
28+
it('disabled prop overrides accessibilityState.disabled', () => {
29+
const tree = renderer
30+
.create(<Slider disabled accessibilityState={{disabled: false}} />)
31+
.toJSON();
32+
33+
expect(tree).toMatchSnapshot();
34+
});
35+
36+
it('disabled prop overrides accessibilityState.enabled', () => {
37+
const tree = renderer
38+
.create(<Slider disabled={false} accessibilityState={{disabled: true}} />)
39+
.toJSON();
40+
41+
expect(tree).toMatchSnapshot();
42+
});
43+
2044
it('renders a slider with custom props', () => {
2145
const tree = renderer
2246
.create(

src/js/__tests__/__snapshots__/Slider.test.js.snap

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,65 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`<Slider /> accessibilityState disabled sets disabled={true} 1`] = `
4+
<RNCSlider
5+
accessibilityState={
6+
Object {
7+
"disabled": true,
8+
}
9+
}
10+
disabled={true}
11+
enabled={false}
12+
inverted={false}
13+
maximumValue={1}
14+
minimumValue={0}
15+
onChange={null}
16+
onRNCSliderSlidingComplete={null}
17+
onRNCSliderSlidingStart={null}
18+
onRNCSliderValueChange={null}
19+
onResponderTerminationRequest={[Function]}
20+
onStartShouldSetResponder={[Function]}
21+
step={0}
22+
style={
23+
Object {
24+
"height": 40,
25+
}
26+
}
27+
tapToSeek={false}
28+
thumbImage={null}
29+
value={0}
30+
/>
31+
`;
32+
33+
exports[`<Slider /> disabled prop overrides accessibilityState.disabled 1`] = `
34+
<RNCSlider
35+
accessibilityState={
36+
Object {
37+
"disabled": true,
38+
}
39+
}
40+
disabled={true}
41+
enabled={false}
42+
inverted={false}
43+
maximumValue={1}
44+
minimumValue={0}
45+
onChange={null}
46+
onRNCSliderSlidingComplete={null}
47+
onRNCSliderSlidingStart={null}
48+
onRNCSliderValueChange={null}
49+
onResponderTerminationRequest={[Function]}
50+
onStartShouldSetResponder={[Function]}
51+
step={0}
52+
style={
53+
Object {
54+
"height": 40,
55+
}
56+
}
57+
tapToSeek={false}
58+
thumbImage={null}
59+
value={0}
60+
/>
61+
`;
62+
363
exports[`<Slider /> renders a slider with custom props 1`] = `
464
<RNCSlider
565
disabled={false}
@@ -30,6 +90,11 @@ exports[`<Slider /> renders a slider with custom props 1`] = `
3090

3191
exports[`<Slider /> renders disabled slider 1`] = `
3292
<RNCSlider
93+
accessibilityState={
94+
Object {
95+
"disabled": true,
96+
}
97+
}
3398
disabled={true}
3499
enabled={false}
35100
inverted={false}
@@ -77,3 +142,33 @@ exports[`<Slider /> renders enabled slider 1`] = `
77142
value={0}
78143
/>
79144
`;
145+
146+
exports[`<Slider /> disabled prop overrides accessibilityState.enabled 1`] = `
147+
<RNCSlider
148+
accessibilityState={
149+
Object {
150+
"disabled": false,
151+
}
152+
}
153+
disabled={false}
154+
enabled={true}
155+
inverted={false}
156+
maximumValue={1}
157+
minimumValue={0}
158+
onChange={null}
159+
onRNCSliderSlidingComplete={null}
160+
onRNCSliderSlidingStart={null}
161+
onRNCSliderValueChange={null}
162+
onResponderTerminationRequest={[Function]}
163+
onStartShouldSetResponder={[Function]}
164+
step={0}
165+
style={
166+
Object {
167+
"height": 40,
168+
}
169+
}
170+
tapToSeek={false}
171+
thumbImage={null}
172+
value={0}
173+
/>
174+
`;

0 commit comments

Comments
 (0)