@ankhorage/surface 3.4.4 → 3.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @ankhorage/surface
2
2
 
3
+ ## 3.4.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 191daf8: Render structured Radio children directly instead of nesting non-text React nodes inside native Text while preserving primitive label typography and radio semantics.
8
+
9
+ ## 3.4.5
10
+
11
+ ### Patch Changes
12
+
13
+ - f297152: Update Ankhorage dependencies: `@ankhorage/contracts`.
14
+ - 7bcf2d9: Update Ankhorage dependencies: `@ankhorage/contracts`.
15
+
3
16
  ## 3.4.4
4
17
 
5
18
  ### Patch Changes
@@ -1,4 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { RadioProps } from './types';
3
+ /***
4
+ * Renders one radio control with either text or structured label content.
5
+ */
3
6
  export declare function Radio({ children, checked, defaultChecked, onCheckedChange, color, size, disabled, invalid, readOnly, accessibilityLabel, testID, ...props }: RadioProps): React.JSX.Element;
4
7
  //# sourceMappingURL=Radio.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Radio.d.ts","sourceRoot":"","sources":["../../../src/components/radio/Radio.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAa1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,wBAAgB,KAAK,CAAC,EACpB,QAAQ,EACR,OAAO,EACP,cAAsB,EACtB,eAAe,EACf,KAAiB,EACjB,IAAU,EACV,QAAgB,EAChB,OAAe,EACf,QAAgB,EAChB,kBAAkB,EAClB,MAAM,EACN,GAAG,KAAK,EACT,EAAE,UAAU,qBAyFZ"}
1
+ {"version":3,"file":"Radio.d.ts","sourceRoot":"","sources":["../../../src/components/radio/Radio.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAc1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C;;GAEG;AACH,wBAAgB,KAAK,CAAC,EACpB,QAAQ,EACR,OAAO,EACP,cAAsB,EACtB,eAAe,EACf,KAAiB,EACjB,IAAU,EACV,QAAgB,EAChB,OAAe,EACf,QAAgB,EAChB,kBAAkB,EAClB,MAAM,EACN,GAAG,KAAK,EACT,EAAE,UAAU,qBA2FZ"}
@@ -5,6 +5,10 @@ import { Box } from '../../layout';
5
5
  import { ButtonBase } from '../../primitives/button-base';
6
6
  import { Text } from '../../primitives/text';
7
7
  import { useTheme } from '../../theme/ThemeContext';
8
+ import { isRadioTextContent } from './isRadioTextContent';
9
+ /***
10
+ * Renders one radio control with either text or structured label content.
11
+ */
8
12
  export function Radio({ children, checked, defaultChecked = false, onCheckedChange, color = 'primary', size = 'm', disabled = false, invalid = false, readOnly = false, accessibilityLabel, testID, ...props }) {
9
13
  const { theme } = useTheme();
10
14
  const [isChecked, setChecked] = useControllableState({
@@ -19,6 +23,7 @@ export function Radio({ children, checked, defaultChecked = false, onCheckedChan
19
23
  kind: 'radio',
20
24
  readOnly,
21
25
  });
26
+ const hasContent = children !== undefined && children !== null && children !== false;
22
27
  return (<ButtonBase {...props} accessibilityLabel={accessibilityLabel} accessibilityRole="radio" accessibilityState={{ checked: isChecked }} disabled={disabled} onPress={nextChecked === null
23
28
  ? undefined
24
29
  : () => {
@@ -39,6 +44,7 @@ export function Radio({ children, checked, defaultChecked = false, onCheckedChan
39
44
  color,
40
45
  });
41
46
  const labelEmphasis = colors.labelColor === theme.semantics.content.muted ? 'muted' : 'default';
47
+ const isTextContent = isRadioTextContent(children);
42
48
  return (<Box style={{
43
49
  alignItems: 'center',
44
50
  flexDirection: 'row',
@@ -59,8 +65,8 @@ export function Radio({ children, checked, defaultChecked = false, onCheckedChan
59
65
  width: indicatorSize.radioDot,
60
66
  }}/>) : null}
61
67
  </Box>
62
- {children ? (<Box ml="s">
63
- <Text emphasis={labelEmphasis}>{children}</Text>
68
+ {hasContent ? (<Box flex={1} ml="s">
69
+ {isTextContent ? <Text emphasis={labelEmphasis}>{children}</Text> : children}
64
70
  </Box>) : null}
65
71
  </Box>);
66
72
  }}
@@ -1 +1 @@
1
- {"version":3,"file":"Radio.js","sourceRoot":"","sources":["../../../src/components/radio/Radio.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\n\nimport {\n resolveFieldState,\n resolveIndicatorSize,\n resolveSelectionControlColors,\n resolveSelectionControlNextChecked,\n} from '../../internal/resolvers';\nimport { useControllableState } from '../../internal/useControllableState';\nimport { Box } from '../../layout';\nimport { ButtonBase } from '../../primitives/button-base';\nimport { Text } from '../../primitives/text';\nimport { useTheme } from '../../theme/ThemeContext';\nimport type { RadioProps } from './types';\n\nexport function Radio({\n children,\n checked,\n defaultChecked = false,\n onCheckedChange,\n color = 'primary',\n size = 'm',\n disabled = false,\n invalid = false,\n readOnly = false,\n accessibilityLabel,\n testID,\n ...props\n}: RadioProps) {\n const { theme } = useTheme();\n const [isChecked, setChecked] = useControllableState<boolean>({\n value: checked,\n defaultValue: defaultChecked,\n onChange: onCheckedChange,\n });\n const indicatorSize = resolveIndicatorSize(size);\n const nextChecked = resolveSelectionControlNextChecked({\n checked: isChecked,\n disabled,\n kind: 'radio',\n readOnly,\n });\n\n return (\n <ButtonBase\n {...props}\n accessibilityLabel={accessibilityLabel}\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: isChecked }}\n disabled={disabled}\n onPress={\n nextChecked === null\n ? undefined\n : () => {\n setChecked(nextChecked);\n }\n }\n testID={testID}\n >\n {(interactionState) => {\n const fieldState = resolveFieldState({\n disabled,\n focused: interactionState.focused,\n invalid,\n readOnly,\n });\n const colors = resolveSelectionControlColors(theme, {\n checked: isChecked,\n fieldState,\n hovered: interactionState.hovered,\n pressed: interactionState.pressed,\n color,\n });\n const labelEmphasis =\n colors.labelColor === theme.semantics.content.muted ? 'muted' : 'default';\n\n return (\n <Box\n style={{\n alignItems: 'center',\n flexDirection: 'row',\n opacity: colors.opacity,\n }}\n >\n <Box\n radius=\"full\"\n style={{\n alignItems: 'center',\n backgroundColor: colors.backgroundColor,\n borderColor: colors.borderColor,\n borderWidth: 1.5,\n height: indicatorSize.radio,\n justifyContent: 'center',\n width: indicatorSize.radio,\n }}\n >\n {isChecked ? (\n <Box\n radius=\"full\"\n style={{\n backgroundColor: colors.indicatorColor,\n height: indicatorSize.radioDot,\n width: indicatorSize.radioDot,\n }}\n />\n ) : null}\n </Box>\n {children ? (\n <Box ml=\"s\">\n <Text emphasis={labelEmphasis}>{children}</Text>\n </Box>\n ) : null}\n </Box>\n );\n }}\n </ButtonBase>\n );\n}\n"]}
1
+ {"version":3,"file":"Radio.js","sourceRoot":"","sources":["../../../src/components/radio/Radio.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\n\nimport {\n resolveFieldState,\n resolveIndicatorSize,\n resolveSelectionControlColors,\n resolveSelectionControlNextChecked,\n} from '../../internal/resolvers';\nimport { useControllableState } from '../../internal/useControllableState';\nimport { Box } from '../../layout';\nimport { ButtonBase } from '../../primitives/button-base';\nimport { Text } from '../../primitives/text';\nimport { useTheme } from '../../theme/ThemeContext';\nimport { isRadioTextContent } from './isRadioTextContent';\nimport type { RadioProps } from './types';\n\n/***\n * Renders one radio control with either text or structured label content.\n */\nexport function Radio({\n children,\n checked,\n defaultChecked = false,\n onCheckedChange,\n color = 'primary',\n size = 'm',\n disabled = false,\n invalid = false,\n readOnly = false,\n accessibilityLabel,\n testID,\n ...props\n}: RadioProps) {\n const { theme } = useTheme();\n const [isChecked, setChecked] = useControllableState<boolean>({\n value: checked,\n defaultValue: defaultChecked,\n onChange: onCheckedChange,\n });\n const indicatorSize = resolveIndicatorSize(size);\n const nextChecked = resolveSelectionControlNextChecked({\n checked: isChecked,\n disabled,\n kind: 'radio',\n readOnly,\n });\n const hasContent = children !== undefined && children !== null && children !== false;\n\n return (\n <ButtonBase\n {...props}\n accessibilityLabel={accessibilityLabel}\n accessibilityRole=\"radio\"\n accessibilityState={{ checked: isChecked }}\n disabled={disabled}\n onPress={\n nextChecked === null\n ? undefined\n : () => {\n setChecked(nextChecked);\n }\n }\n testID={testID}\n >\n {(interactionState) => {\n const fieldState = resolveFieldState({\n disabled,\n focused: interactionState.focused,\n invalid,\n readOnly,\n });\n const colors = resolveSelectionControlColors(theme, {\n checked: isChecked,\n fieldState,\n hovered: interactionState.hovered,\n pressed: interactionState.pressed,\n color,\n });\n const labelEmphasis =\n colors.labelColor === theme.semantics.content.muted ? 'muted' : 'default';\n const isTextContent = isRadioTextContent(children);\n\n return (\n <Box\n style={{\n alignItems: 'center',\n flexDirection: 'row',\n opacity: colors.opacity,\n }}\n >\n <Box\n radius=\"full\"\n style={{\n alignItems: 'center',\n backgroundColor: colors.backgroundColor,\n borderColor: colors.borderColor,\n borderWidth: 1.5,\n height: indicatorSize.radio,\n justifyContent: 'center',\n width: indicatorSize.radio,\n }}\n >\n {isChecked ? (\n <Box\n radius=\"full\"\n style={{\n backgroundColor: colors.indicatorColor,\n height: indicatorSize.radioDot,\n width: indicatorSize.radioDot,\n }}\n />\n ) : null}\n </Box>\n {hasContent ? (\n <Box flex={1} ml=\"s\">\n {isTextContent ? <Text emphasis={labelEmphasis}>{children}</Text> : children}\n </Box>\n ) : null}\n </Box>\n );\n }}\n </ButtonBase>\n );\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type React from 'react';
2
+ /***
3
+ * Reports whether Radio content should use the built-in text-label presentation.
4
+ */
5
+ export declare function isRadioTextContent(children: React.ReactNode): children is string | number;
6
+ //# sourceMappingURL=isRadioTextContent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isRadioTextContent.d.ts","sourceRoot":"","sources":["../../../src/components/radio/isRadioTextContent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,GAAG,QAAQ,IAAI,MAAM,GAAG,MAAM,CAEzF"}
@@ -0,0 +1,7 @@
1
+ /***
2
+ * Reports whether Radio content should use the built-in text-label presentation.
3
+ */
4
+ export function isRadioTextContent(children) {
5
+ return typeof children === 'string' || typeof children === 'number';
6
+ }
7
+ //# sourceMappingURL=isRadioTextContent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isRadioTextContent.js","sourceRoot":"","sources":["../../../src/components/radio/isRadioTextContent.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAyB;IAC1D,OAAO,OAAO,QAAQ,KAAK,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,CAAC;AACtE,CAAC","sourcesContent":["import type React from 'react';\n\n/***\n * Reports whether Radio content should use the built-in text-label presentation.\n */\nexport function isRadioTextContent(children: React.ReactNode): children is string | number {\n return typeof children === 'string' || typeof children === 'number';\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/surface",
3
3
  "type": "module",
4
- "version": "3.4.4",
4
+ "version": "3.4.6",
5
5
  "description": "Stable UI foundation for React Native and React Native Web.",
6
6
  "homepage": "https://github.com/ankhorage/surface#readme",
7
7
  "bugs": {
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@ankhorage/color-theory": "^0.3.0",
43
- "@ankhorage/contracts": "^11.0.0"
43
+ "@ankhorage/contracts": "^12.0.1"
44
44
  },
45
45
  "files": [
46
46
  "dist",
@@ -86,7 +86,7 @@
86
86
  "version-packages": "ankhorage-changeset version"
87
87
  },
88
88
  "devDependencies": {
89
- "@ankhorage/devtools": "^1.11.7",
89
+ "@ankhorage/devtools": "^1.14.0",
90
90
  "@ankhorage/paradox": "^0.1.24",
91
91
  "@react-native-vector-icons/fontawesome": "^13.1.3",
92
92
  "@react-native-vector-icons/fontawesome5": "^13.1.3",
@@ -0,0 +1,20 @@
1
+ import { expect, test } from 'bun:test';
2
+ import React from 'react';
3
+
4
+ import { isRadioTextContent } from './isRadioTextContent';
5
+
6
+ test('classifies only primitive labels for the built-in text presentation', () => {
7
+ expect(isRadioTextContent('Option')).toBe(true);
8
+ expect(isRadioTextContent(7)).toBe(true);
9
+ expect(isRadioTextContent(React.createElement('span', null, 'Structured'))).toBe(false);
10
+ expect(isRadioTextContent(null)).toBe(false);
11
+ });
12
+
13
+ test('Radio preserves one radio interaction boundary and renders structured content directly', async () => {
14
+ const source = await Bun.file('src/components/radio/Radio.tsx').text();
15
+
16
+ expect(source).toContain('accessibilityRole="radio"');
17
+ expect(source).toContain('accessibilityState={{ checked: isChecked }}');
18
+ expect(source).toContain('const isTextContent = isRadioTextContent(children);');
19
+ expect(source).toMatch(/isTextContent \? <Text[^>]*>\{children\}<\/Text> : children/);
20
+ });
@@ -11,8 +11,12 @@ import { Box } from '../../layout';
11
11
  import { ButtonBase } from '../../primitives/button-base';
12
12
  import { Text } from '../../primitives/text';
13
13
  import { useTheme } from '../../theme/ThemeContext';
14
+ import { isRadioTextContent } from './isRadioTextContent';
14
15
  import type { RadioProps } from './types';
15
16
 
17
+ /***
18
+ * Renders one radio control with either text or structured label content.
19
+ */
16
20
  export function Radio({
17
21
  children,
18
22
  checked,
@@ -40,6 +44,7 @@ export function Radio({
40
44
  kind: 'radio',
41
45
  readOnly,
42
46
  });
47
+ const hasContent = children !== undefined && children !== null && children !== false;
43
48
 
44
49
  return (
45
50
  <ButtonBase
@@ -73,6 +78,7 @@ export function Radio({
73
78
  });
74
79
  const labelEmphasis =
75
80
  colors.labelColor === theme.semantics.content.muted ? 'muted' : 'default';
81
+ const isTextContent = isRadioTextContent(children);
76
82
 
77
83
  return (
78
84
  <Box
@@ -105,9 +111,9 @@ export function Radio({
105
111
  />
106
112
  ) : null}
107
113
  </Box>
108
- {children ? (
109
- <Box ml="s">
110
- <Text emphasis={labelEmphasis}>{children}</Text>
114
+ {hasContent ? (
115
+ <Box flex={1} ml="s">
116
+ {isTextContent ? <Text emphasis={labelEmphasis}>{children}</Text> : children}
111
117
  </Box>
112
118
  ) : null}
113
119
  </Box>
@@ -0,0 +1,8 @@
1
+ import type React from 'react';
2
+
3
+ /***
4
+ * Reports whether Radio content should use the built-in text-label presentation.
5
+ */
6
+ export function isRadioTextContent(children: React.ReactNode): children is string | number {
7
+ return typeof children === 'string' || typeof children === 'number';
8
+ }