diff --git a/docs/6.x/docs/components/Switch/Switch.mdx b/docs/6.x/docs/components/Switch/Switch.mdx index 41df316693..ea9d6ff305 100644 --- a/docs/6.x/docs/components/Switch/Switch.mdx +++ b/docs/6.x/docs/components/Switch/Switch.mdx @@ -29,11 +29,12 @@ const Example = () => { ## Theming Customize by overriding these `theme.colors` roles: -- `primary` / `onPrimary`: selected track + icon / selected handle -- `primaryContainer`: selected handle on hover, press +- `primary` / `onPrimary`: selected track / selected handle +- `onPrimaryContainer`: selected icon +- `primaryContainer`: selected handle on hover, focus, press - `surfaceContainerHighest`: unselected track + icon - `outline`: unselected resting handle, unselected track outline -- `onSurfaceVariant`: unselected handle on hover, press +- `onSurfaceVariant`: unselected handle on hover, focus, press - `onSurface`: disabled track, handle, and icon fills - `surface`: disabled selected handle - `secondary`: focus indicator @@ -54,22 +55,6 @@ Customize by overriding these `theme.colors` roles:
-### onValueChange - -
- - - -
- -### disabled - -
- - - -
- ### checkedIcon
@@ -116,6 +101,30 @@ Customize by overriding these `theme.colors` roles: +
+ +### onValueChange + +
+ + + +
+ +### readOnly + +
+ + + +
+ +### disabled + +
+ + + diff --git a/docs/6.x/docs/guides/migration.md b/docs/6.x/docs/guides/migration.md index e488b101ef..89d8a726e8 100644 --- a/docs/6.x/docs/guides/migration.md +++ b/docs/6.x/docs/guides/migration.md @@ -280,3 +280,39 @@ const theme = { style={{ fontSize: 16, color: '#1C1B1F' }} /> ``` + +### Switch + +#### Explicit operability + +A `Switch` now has to declare how it can be operated. Previously a switch with no `onValueChange` still rendered as an enabled, focusable control that did nothing when activated, and screen readers announced it as operable. + +Pass `onValueChange` to make it interactive, or mark it `readOnly` or `disabled` to render it as a state indicator. A read-only switch keeps its enabled appearance and is still announced with its on/off state, but it is neither focusable nor pressable — it is not reported as disabled. + +```tsx +// Before (v5) — an enabled switch that does nothing + + +// After (v6) — say which it is + + + +``` + +This most often shows up where the switch sits inside a row that owns the press: + +```tsx +// After (v6) + + + Dark Theme + + + + + +``` + +#### Touch target height + +`Switch` now reserves the 48dp minimum touch target Material Design requires, so it occupies 48dp of height instead of 40dp. Nothing painted changed size — the track is still 52×32 — but rows containing a switch may become slightly taller. diff --git a/docs/src/data/componentDocs6x.json b/docs/src/data/componentDocs6x.json index b223c9c0a8..a3e14635b9 100644 --- a/docs/src/data/componentDocs6x.json +++ b/docs/src/data/componentDocs6x.json @@ -11354,10 +11354,10 @@ "Switch/Switch": { "filepath": "Switch/Switch.tsx", "title": "Switch", - "description": "Material 3 toggle between two mutually exclusive states (on / off).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Switch } from 'react-native-paper';\n\nconst Example = () => {\n const [on, setOn] = React.useState(false);\n return ;\n};\n```\n\n## Theming\nCustomize by overriding these `theme.colors` roles:\n- `primary` / `onPrimary`: selected track + icon / selected handle\n- `primaryContainer`: selected handle on hover, press\n- `surfaceContainerHighest`: unselected track + icon\n- `outline`: unselected resting handle, unselected track outline\n- `onSurfaceVariant`: unselected handle on hover, press\n- `onSurface`: disabled track, handle, and icon fills\n- `surface`: disabled selected handle\n- `secondary`: focus indicator", + "description": "Material 3 toggle between two mutually exclusive states (on / off).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Switch } from 'react-native-paper';\n\nconst Example = () => {\n const [on, setOn] = React.useState(false);\n return ;\n};\n```\n\n## Theming\nCustomize by overriding these `theme.colors` roles:\n- `primary` / `onPrimary`: selected track / selected handle\n- `onPrimaryContainer`: selected icon\n- `primaryContainer`: selected handle on hover, focus, press\n- `surfaceContainerHighest`: unselected track + icon\n- `outline`: unselected resting handle, unselected track outline\n- `onSurfaceVariant`: unselected handle on hover, focus, press\n- `onSurface`: disabled track, handle, and icon fills\n- `surface`: disabled selected handle\n- `secondary`: focus indicator", "link": "switch", "data": { - "description": "Material 3 toggle between two mutually exclusive states (on / off).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Switch } from 'react-native-paper';\n\nconst Example = () => {\n const [on, setOn] = React.useState(false);\n return ;\n};\n```\n\n## Theming\nCustomize by overriding these `theme.colors` roles:\n- `primary` / `onPrimary`: selected track + icon / selected handle\n- `primaryContainer`: selected handle on hover, press\n- `surfaceContainerHighest`: unselected track + icon\n- `outline`: unselected resting handle, unselected track outline\n- `onSurfaceVariant`: unselected handle on hover, press\n- `onSurface`: disabled track, handle, and icon fills\n- `surface`: disabled selected handle\n- `secondary`: focus indicator", + "description": "Material 3 toggle between two mutually exclusive states (on / off).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Switch } from 'react-native-paper';\n\nconst Example = () => {\n const [on, setOn] = React.useState(false);\n return ;\n};\n```\n\n## Theming\nCustomize by overriding these `theme.colors` roles:\n- `primary` / `onPrimary`: selected track / selected handle\n- `onPrimaryContainer`: selected icon\n- `primaryContainer`: selected handle on hover, focus, press\n- `surfaceContainerHighest`: unselected track + icon\n- `outline`: unselected resting handle, unselected track outline\n- `onSurfaceVariant`: unselected handle on hover, focus, press\n- `onSurface`: disabled track, handle, and icon fills\n- `surface`: disabled selected handle\n- `secondary`: focus indicator", "displayName": "Switch", "methods": [], "statics": [], @@ -11369,35 +11369,6 @@ }, "description": "Whether the switch is on." }, - "onValueChange": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(value: boolean) => void", - "signature": { - "arguments": [ - { - "name": "value", - "type": { - "name": "boolean" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Called with the new value when the user toggles the switch." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Disables interaction and renders the disabled visual state." - }, "checkedIcon": { "required": false, "tsType": { @@ -11445,6 +11416,42 @@ "name": "string" }, "description": "Accessibility label for the switch. This is read by the screen reader when the user focuses the switch." + }, + "onValueChange": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(value: boolean) => void", + "signature": { + "arguments": [ + { + "name": "value", + "type": { + "name": "boolean" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Called with the new value when the user toggles the switch. Required\nunless the switch is `readOnly` or `disabled`." + }, + "readOnly": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Reports state the user cannot change here. The switch keeps its enabled\nappearance and is still announced by a screen reader, but it is neither\nfocusable nor pressable." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Disables interaction and renders the disabled visual state." } } }, diff --git a/example/src/DrawerItems.tsx b/example/src/DrawerItems.tsx index 94afa3136c..00489864cc 100644 --- a/example/src/DrawerItems.tsx +++ b/example/src/DrawerItems.tsx @@ -178,7 +178,7 @@ function DrawerItems() { Use Dynamic Theme - + @@ -187,7 +187,7 @@ function DrawerItems() { Dark Theme - + @@ -196,7 +196,7 @@ function DrawerItems() { RTL - + @@ -205,7 +205,7 @@ function DrawerItems() { Collapsed drawer * - + @@ -214,7 +214,7 @@ function DrawerItems() { Custom font * - + @@ -225,7 +225,7 @@ function DrawerItems() { {isIOS ? 'Highlight' : 'Ripple'} effect * - + diff --git a/example/src/Examples/FABExample.tsx b/example/src/Examples/FABExample.tsx index 09ce39aa14..1b8c3d61f9 100644 --- a/example/src/Examples/FABExample.tsx +++ b/example/src/Examples/FABExample.tsx @@ -154,7 +154,7 @@ const FABExample = () => { title="Show FAB" right={() => ( - + )} onPress={() => setShowFab((v) => !v)} diff --git a/example/src/Examples/SwitchExample.tsx b/example/src/Examples/SwitchExample.tsx index 545dbf8dd3..981013d44c 100644 --- a/example/src/Examples/SwitchExample.tsx +++ b/example/src/Examples/SwitchExample.tsx @@ -88,6 +88,10 @@ const SwitchExample = () => { /> + + + + { {label} - + diff --git a/src/components/Switch/Switch.tsx b/src/components/Switch/Switch.tsx index b0ca4b4bd7..be8726b194 100644 --- a/src/components/Switch/Switch.tsx +++ b/src/components/Switch/Switch.tsx @@ -3,7 +3,9 @@ import { Platform, Pressable, StyleSheet, + type NativeSyntheticEvent, type StyleProp, + type TargetedEvent, View, type ViewStyle, } from 'react-native'; @@ -15,7 +17,6 @@ import Animated, { useAnimatedStyle, useDerivedValue, useSharedValue, - withDelay, withSequence, withSpring, withTiming, @@ -33,19 +34,11 @@ import type { StateOpacityKey, ThemeProp } from '../../types'; import { isKeyboardFocusEvent } from '../../utils/isKeyboardFocusEvent'; import Icon, { type IconSource } from '../Icon'; -export type Props = { +type SwitchBaseProps = { /** * Whether the switch is on. */ value?: boolean; - /** - * Called with the new value when the user toggles the switch. - */ - onValueChange?: (value: boolean) => void; - /** - * Disables interaction and renders the disabled visual state. - */ - disabled?: boolean; /** * Icon shown inside the handle when checked */ @@ -63,11 +56,51 @@ export type Props = { 'aria-label'?: string; }; +export type Props = SwitchBaseProps & { + /** + * Called with the new value when the user toggles the switch. Required + * unless the switch is `readOnly` or `disabled`. + */ + onValueChange?: (value: boolean) => void; + /** + * Reports state the user cannot change here. The switch keeps its enabled + * appearance and is still announced by a screen reader, but it is neither + * focusable nor pressable. + */ + readOnly?: boolean; + /** + * Disables interaction and renders the disabled visual state. + */ + disabled?: boolean; +}; + +/** + * `Props`, narrowed so a switch always declares how it can be operated and can + * never render as an enabled control that does nothing when activated. + * + * This is applied to the component's declared type rather than folded into + * `Props`, because the docs generator reads props off the parameter annotation + * and silently drops a union (a top-level one drops the whole page). So the + * parameter below stays annotated as the flat `Props`, and callers still get + * the narrowed type through this one. + */ +export type OperableProps = SwitchBaseProps & + ( + | { + onValueChange: (value: boolean) => void; + readOnly?: false; + disabled?: boolean; + } + | { onValueChange?: never; readOnly: true; disabled?: boolean } + | { onValueChange?: never; readOnly?: false; disabled: true } + ); + const { trackWidth: TRACK_WIDTH, trackHeight: TRACK_HEIGHT, trackOutlineWidth: TRACK_OUTLINE_WIDTH, stateLayerSize: STATE_LAYER_SIZE, + touchTargetSize: TOUCH_TARGET_SIZE, selectedHandleSize: SELECTED_HANDLE, unselectedHandleSize: UNSELECTED_HANDLE, iconHandleSize: ICON_HANDLE, @@ -86,11 +119,10 @@ const stateOpacity = stateTokens.opacity; const { thickness: FOCUS_THICKNESS, outerOffset: FOCUS_OUTER_OFFSET } = stateTokens.focusIndicator; const FOCUS_RING_INSET = -(FOCUS_OUTER_OFFSET + FOCUS_THICKNESS); -const OVERLAY_TOP = (STATE_LAYER_SIZE - TRACK_HEIGHT) / 2; - -// Hold-then-grow: a brief delay before snapping to PRESSED_HANDLE so a quick -// tap doesn't flash the press-grow visual. -const PRESS_GROW_DELAY = 100; +// Every painted layer is smaller than the touch target and absolutely +// positioned, so each one is centred against it. +const centreY = (size: number) => (TOUCH_TARGET_SIZE - size) / 2; +const TRACK_TOP = centreY(TRACK_HEIGHT); function restingHandleSize(checked: boolean, hasIcon: boolean): number { if (hasIcon) return ICON_HANDLE; @@ -117,18 +149,20 @@ const CHECKED_CENTER = TRACK_WIDTH - HANDLE_PADDING - SELECTED_HANDLE / 2; * * ## Theming * Customize by overriding these `theme.colors` roles: - * - `primary` / `onPrimary`: selected track + icon / selected handle - * - `primaryContainer`: selected handle on hover, press + * - `primary` / `onPrimary`: selected track / selected handle + * - `onPrimaryContainer`: selected icon + * - `primaryContainer`: selected handle on hover, focus, press * - `surfaceContainerHighest`: unselected track + icon * - `outline`: unselected resting handle, unselected track outline - * - `onSurfaceVariant`: unselected handle on hover, press + * - `onSurfaceVariant`: unselected handle on hover, focus, press * - `onSurface`: disabled track, handle, and icon fills * - `surface`: disabled selected handle * - `secondary`: focus indicator */ -const Switch = ({ +const Switch: (props: OperableProps) => React.JSX.Element = ({ value, disabled, + readOnly, onValueChange, checkedIcon, uncheckedIcon, @@ -145,6 +179,18 @@ const Switch = ({ const checked = !!value; const isDisabled = !!disabled; const isEnabled = !isDisabled; + const isReadOnly = !!readOnly; + // `OperableProps` already requires one of these, but untyped callers can slip + // past it, so guard here too rather than rendering an enabled no-op. + const isMissingOperability = !onValueChange && !isReadOnly && !isDisabled; + + if (isMissingOperability) { + console.warn( + 'Switch: pass `onValueChange` to make the switch operable, or set `readOnly` or `disabled` to render it as a state indicator.' + ); + } + + const isInteractive = !isDisabled && !isReadOnly && !isMissingOperability; const iconSource = checked ? checkedIcon : uncheckedIcon; const hasIcon = iconSource !== undefined; @@ -229,14 +275,10 @@ const Switch = ({ ({ p, c, hi }) => { const restingSize = hi === 1 ? ICON_HANDLE : c === 1 ? SELECTED_HANDLE : UNSELECTED_HANDLE; - if (p === 1) { - handleSize.value = withDelay( - PRESS_GROW_DELAY, - withTiming(PRESSED_HANDLE, { duration: 0 }) - ); - } else { - handleSize.value = withSpring(restingSize, springConfig); - } + handleSize.value = withSpring( + p === 1 ? PRESSED_HANDLE : restingSize, + springConfig + ); }, [springConfig] ); @@ -251,12 +293,7 @@ const Switch = ({ (current, prev) => { if (current === prev) return; const wasPressed = prev === 'pressed'; - const target = - current === 'pressed' - ? stateOpacity.pressed - : current === 'hovered' - ? stateOpacity.hovered - : 0; + const target = current ? stateOpacity[current] : 0; if (wasPressed && current !== 'pressed') { // On release: rise to peak, then fall to the next state. stateLayerAlpha.value = withSequence( @@ -284,6 +321,11 @@ const Switch = ({ ? colors.checkedPressedHandleColor : colors.uncheckedPressedHandleColor; } + if (focusedSV.value === 1) { + return isCheckedNow + ? colors.checkedFocusHandleColor + : colors.uncheckedFocusHandleColor; + } if (hoveredSV.value === 1) { return isCheckedNow ? colors.checkedHoverHandleColor @@ -297,7 +339,7 @@ const Switch = ({ const handleAnimatedStyle = useAnimatedStyle(() => ({ width: handleSize.value, height: handleSize.value, - top: (STATE_LAYER_SIZE - handleSize.value) / 2, + top: (TOUCH_TARGET_SIZE - handleSize.value) / 2, transform: [ { translateX: xSign * (handleCenter.value - handleSize.value / 2) }, ], @@ -345,30 +387,40 @@ const Switch = ({ const trackOpacityValue = isDisabled ? DISABLED_TRACK_OPACITY : 1; const iconSize = checked ? SELECTED_ICON : UNSELECTED_ICON; - return ( - - onValueChange?.(!checked)} - onPressIn={() => { + // A non-interactive switch gets no press, hover, or focus affordances at all. + // It stays in the accessibility tree, so its state is still announced. + const interactionProps = isInteractive + ? { + onPress: () => onValueChange?.(!checked), + onPressIn: () => { pressedSV.value = 1; - }} - onPressOut={() => { + }, + onPressOut: () => { pressedSV.value = 0; - }} - onHoverIn={() => { + }, + onHoverIn: () => { hoveredSV.value = 1; - }} - onHoverOut={() => { + }, + onHoverOut: () => { hoveredSV.value = 0; - }} - onFocus={(e) => { + }, + onFocus: (e: NativeSyntheticEvent) => { if (!isKeyboardFocusEvent(e)) return; focusedSV.value = 1; - }} - onBlur={() => { + }, + onBlur: () => { focusedSV.value = 0; - }} + }, + } + : null; + + return ( + + ) : null} { it('renders on', async () => { - expect((await render()).toJSON()).toMatchSnapshot(); + expect( + (await render()).toJSON() + ).toMatchSnapshot(); }); it('renders off', async () => { - expect((await render()).toJSON()).toMatchSnapshot(); + expect( + ( + await render() + ).toJSON() + ).toMatchSnapshot(); }); it('renders disabled on', async () => { @@ -26,14 +37,25 @@ describe('Switch render', () => { it('renders with checked icon', async () => { expect( - (await render()).toJSON() + ( + await render( + + ) + ).toJSON() ).toMatchSnapshot(); }); it('renders with per-state icons', async () => { expect( ( - await render() + await render( + + ) ).toJSON() ).toMatchSnapshot(); }); @@ -41,10 +63,110 @@ describe('Switch render', () => { describe('Switch accessibility', () => { it('has switch role', async () => { - await render(); + await render(); expect(screen.getByRole('switch')).toBeOnTheScreen(); }); + + it('exposes a touch target meeting the 48dp minimum', async () => { + await render(); + + expect(screen.getByRole('switch')).toHaveStyle({ width: 52, height: 48 }); + }); +}); + +describe('Switch focus state', () => { + const renderAndFocus = async (element: React.ReactElement) => { + await render(element); + + await fireEvent(screen.getByTestId('switch'), 'focus'); + await jest.runAllTimersAsync(); + }; + + const animatedStyle = (testID: string) => + Reanimated.getAnimatedStyle(screen.getByTestId(testID)); + + it('shows the focus indicator on keyboard focus', async () => { + await renderAndFocus( + + ); + + expect(animatedStyle('switch-focus-ring')).toMatchObject({ opacity: 1 }); + }); + + it('hides the focus indicator again on blur', async () => { + await renderAndFocus( + + ); + + await fireEvent(screen.getByTestId('switch'), 'blur'); + await jest.runAllTimersAsync(); + + expect(animatedStyle('switch-focus-ring')).toMatchObject({ opacity: 0 }); + }); + + it('raises the state layer to the focused opacity', async () => { + await renderAndFocus( + + ); + + expect(animatedStyle('switch-state-layer')).toMatchObject({ + opacity: tokens.md.sys.state.opacity.focused, + }); + }); + + it('paints the focus handle color when selected and focused', async () => { + await renderAndFocus( + + ); + + expect(animatedStyle('switch-handle')).toMatchObject({ + backgroundColor: defaultThemes.light.colors.primaryContainer, + }); + }); +}); + +describe('Switch operability', () => { + it('warns when it is given no way to be operated', async () => { + jest.spyOn(console, 'warn').mockImplementation(() => {}); + + // @ts-expect-error -- the props type requires a handler, `readOnly`, or + // `disabled`; this covers untyped callers that get past it. + await render(); + + expect(console.warn).toHaveBeenCalledWith( + expect.stringContaining('onValueChange') + ); + + jest.restoreAllMocks(); + }); + + it('is not focusable when it has no way to be operated', async () => { + jest.spyOn(console, 'warn').mockImplementation(() => {}); + + // @ts-expect-error -- see above. + await render(); + + expect(screen.getByRole('switch')).toHaveProp('focusable', false); + + jest.restoreAllMocks(); + }); + + it('announces a read-only switch without making it focusable', async () => { + await render(); + + const control = screen.getByRole('switch'); + + expect(control).toHaveProp('focusable', false); + expect(control).toBeChecked(); + expect(control).toBeEnabled(); + }); + + it('keeps an interactive switch focusable', async () => { + await render(); + + expect(screen.getByRole('switch')).toHaveProp('focusable', true); + }); }); describe('Switch interaction', () => { diff --git a/src/components/__tests__/__snapshots__/Switch.test.tsx.snap b/src/components/__tests__/__snapshots__/Switch.test.tsx.snap index e38a5145e1..45aed5023f 100644 --- a/src/components/__tests__/__snapshots__/Switch.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Switch.test.tsx.snap @@ -6,7 +6,7 @@ exports[`Switch render renders disabled off 1`] = ` [ { "alignItems": "center", - "height": 40, + "height": 48, "justifyContent": "center", "overflow": "visible", "width": 52, @@ -34,8 +34,9 @@ exports[`Switch render renders disabled off 1`] = ` } } accessible={true} + aria-readonly={false} collapsable={false} - focusable={true} + focusable={false} onBlur={[Function]} onClick={[Function]} onFocus={[Function]} @@ -50,7 +51,7 @@ exports[`Switch render renders disabled off 1`] = ` [ { "alignItems": "center", - "height": 40, + "height": 48, "justifyContent": "center", "width": 52, }, @@ -104,7 +105,7 @@ exports[`Switch render renders disabled off 1`] = ` "height": 40, "pointerEvents": "none", "position": "absolute", - "top": 0, + "top": 4, "width": 40, }, { @@ -139,7 +140,7 @@ exports[`Switch render renders disabled off 1`] = ` }, { "height": 16, - "top": 12, + "top": 16, "transform": [ { "translateX": 8, @@ -201,10 +202,10 @@ exports[`Switch render renders disabled off 1`] = ` "borderColor": "rgba(98, 91, 113, 1)", "borderRadius": 9999, "borderWidth": 3, - "bottom": -1, + "bottom": 3, "left": -5, "right": -5, - "top": -1, + "top": 3, }, { "opacity": 0, @@ -221,7 +222,7 @@ exports[`Switch render renders disabled on 1`] = ` [ { "alignItems": "center", - "height": 40, + "height": 48, "justifyContent": "center", "overflow": "visible", "width": 52, @@ -249,8 +250,9 @@ exports[`Switch render renders disabled on 1`] = ` } } accessible={true} + aria-readonly={false} collapsable={false} - focusable={true} + focusable={false} onBlur={[Function]} onClick={[Function]} onFocus={[Function]} @@ -265,7 +267,7 @@ exports[`Switch render renders disabled on 1`] = ` [ { "alignItems": "center", - "height": 40, + "height": 48, "justifyContent": "center", "width": 52, }, @@ -299,7 +301,7 @@ exports[`Switch render renders disabled on 1`] = ` "height": 40, "pointerEvents": "none", "position": "absolute", - "top": 0, + "top": 4, "width": 40, }, { @@ -334,7 +336,7 @@ exports[`Switch render renders disabled on 1`] = ` }, { "height": 24, - "top": 8, + "top": 12, "transform": [ { "translateX": 24, @@ -396,10 +398,10 @@ exports[`Switch render renders disabled on 1`] = ` "borderColor": "rgba(98, 91, 113, 1)", "borderRadius": 9999, "borderWidth": 3, - "bottom": -1, + "bottom": 3, "left": -5, "right": -5, - "top": -1, + "top": 3, }, { "opacity": 0, @@ -416,7 +418,7 @@ exports[`Switch render renders off 1`] = ` [ { "alignItems": "center", - "height": 40, + "height": 48, "justifyContent": "center", "overflow": "visible", "width": 52, @@ -444,6 +446,7 @@ exports[`Switch render renders off 1`] = ` } } accessible={true} + aria-readonly={false} collapsable={false} focusable={true} onBlur={[Function]} @@ -460,7 +463,7 @@ exports[`Switch render renders off 1`] = ` [ { "alignItems": "center", - "height": 40, + "height": 48, "justifyContent": "center", "width": 52, }, @@ -514,7 +517,7 @@ exports[`Switch render renders off 1`] = ` "height": 40, "pointerEvents": "none", "position": "absolute", - "top": 0, + "top": 4, "width": 40, }, { @@ -549,7 +552,7 @@ exports[`Switch render renders off 1`] = ` }, { "height": 16, - "top": 12, + "top": 16, "transform": [ { "translateX": 8, @@ -594,10 +597,10 @@ exports[`Switch render renders off 1`] = ` "borderColor": "rgba(98, 91, 113, 1)", "borderRadius": 9999, "borderWidth": 3, - "bottom": -1, + "bottom": 3, "left": -5, "right": -5, - "top": -1, + "top": 3, }, { "opacity": 0, @@ -614,7 +617,7 @@ exports[`Switch render renders on 1`] = ` [ { "alignItems": "center", - "height": 40, + "height": 48, "justifyContent": "center", "overflow": "visible", "width": 52, @@ -642,6 +645,7 @@ exports[`Switch render renders on 1`] = ` } } accessible={true} + aria-readonly={false} collapsable={false} focusable={true} onBlur={[Function]} @@ -658,7 +662,7 @@ exports[`Switch render renders on 1`] = ` [ { "alignItems": "center", - "height": 40, + "height": 48, "justifyContent": "center", "width": 52, }, @@ -692,7 +696,7 @@ exports[`Switch render renders on 1`] = ` "height": 40, "pointerEvents": "none", "position": "absolute", - "top": 0, + "top": 4, "width": 40, }, { @@ -727,7 +731,7 @@ exports[`Switch render renders on 1`] = ` }, { "height": 24, - "top": 8, + "top": 12, "transform": [ { "translateX": 24, @@ -772,10 +776,10 @@ exports[`Switch render renders on 1`] = ` "borderColor": "rgba(98, 91, 113, 1)", "borderRadius": 9999, "borderWidth": 3, - "bottom": -1, + "bottom": 3, "left": -5, "right": -5, - "top": -1, + "top": 3, }, { "opacity": 0, @@ -792,7 +796,7 @@ exports[`Switch render renders with checked icon 1`] = ` [ { "alignItems": "center", - "height": 40, + "height": 48, "justifyContent": "center", "overflow": "visible", "width": 52, @@ -820,6 +824,7 @@ exports[`Switch render renders with checked icon 1`] = ` } } accessible={true} + aria-readonly={false} collapsable={false} focusable={true} onBlur={[Function]} @@ -836,7 +841,7 @@ exports[`Switch render renders with checked icon 1`] = ` [ { "alignItems": "center", - "height": 40, + "height": 48, "justifyContent": "center", "width": 52, }, @@ -870,7 +875,7 @@ exports[`Switch render renders with checked icon 1`] = ` "height": 40, "pointerEvents": "none", "position": "absolute", - "top": 0, + "top": 4, "width": 40, }, { @@ -905,7 +910,7 @@ exports[`Switch render renders with checked icon 1`] = ` }, { "height": 24, - "top": 8, + "top": 12, "transform": [ { "translateX": 24, @@ -946,7 +951,7 @@ exports[`Switch render renders with checked icon 1`] = ` "height": 16, "pointerEvents": "none", "position": "absolute", - "top": 12, + "top": 16, "width": 16, }, { @@ -986,7 +991,7 @@ exports[`Switch render renders with checked icon 1`] = ` style={ [ { - "color": "rgba(103, 80, 164, 1)", + "color": "rgba(33, 0, 93, 1)", "fontSize": 16, }, [ @@ -1021,10 +1026,10 @@ exports[`Switch render renders with checked icon 1`] = ` "borderColor": "rgba(98, 91, 113, 1)", "borderRadius": 9999, "borderWidth": 3, - "bottom": -1, + "bottom": 3, "left": -5, "right": -5, - "top": -1, + "top": 3, }, { "opacity": 0, @@ -1041,7 +1046,7 @@ exports[`Switch render renders with per-state icons 1`] = ` [ { "alignItems": "center", - "height": 40, + "height": 48, "justifyContent": "center", "overflow": "visible", "width": 52, @@ -1069,6 +1074,7 @@ exports[`Switch render renders with per-state icons 1`] = ` } } accessible={true} + aria-readonly={false} collapsable={false} focusable={true} onBlur={[Function]} @@ -1085,7 +1091,7 @@ exports[`Switch render renders with per-state icons 1`] = ` [ { "alignItems": "center", - "height": 40, + "height": 48, "justifyContent": "center", "width": 52, }, @@ -1119,7 +1125,7 @@ exports[`Switch render renders with per-state icons 1`] = ` "height": 40, "pointerEvents": "none", "position": "absolute", - "top": 0, + "top": 4, "width": 40, }, { @@ -1154,7 +1160,7 @@ exports[`Switch render renders with per-state icons 1`] = ` }, { "height": 24, - "top": 8, + "top": 12, "transform": [ { "translateX": 24, @@ -1195,7 +1201,7 @@ exports[`Switch render renders with per-state icons 1`] = ` "height": 16, "pointerEvents": "none", "position": "absolute", - "top": 12, + "top": 16, "width": 16, }, { @@ -1235,7 +1241,7 @@ exports[`Switch render renders with per-state icons 1`] = ` style={ [ { - "color": "rgba(103, 80, 164, 1)", + "color": "rgba(33, 0, 93, 1)", "fontSize": 16, }, [ @@ -1270,10 +1276,10 @@ exports[`Switch render renders with per-state icons 1`] = ` "borderColor": "rgba(98, 91, 113, 1)", "borderRadius": 9999, "borderWidth": 3, - "bottom": -1, + "bottom": 3, "left": -5, "right": -5, - "top": -1, + "top": 3, }, { "opacity": 0, diff --git a/src/index.tsx b/src/index.tsx index 8863e2fa20..8571269cb7 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -127,7 +127,7 @@ export type { Props as RadioButtonItemProps } from './components/RadioButton/Rad export type { Props as SearchbarProps } from './components/Searchbar'; export type { Props as SnackbarProps } from './components/Snackbar'; export type { Props as SurfaceProps } from './components/Surface'; -export type { Props as SwitchProps } from './components/Switch/Switch'; +export type { OperableProps as SwitchProps } from './components/Switch/Switch'; export type { TextInputProps, TextInputRenderProps,