@ankhorage/surface 9.1.0 → 9.1.2
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 +12 -0
- package/README.md +1 -1
- package/dist/features/tabs/adapters/inbound/Tab.d.ts.map +1 -1
- package/dist/features/tabs/adapters/inbound/Tab.js +1 -3
- package/dist/features/tabs/adapters/inbound/Tab.js.map +1 -1
- package/package.json +2 -2
- package/src/features/tabs/adapters/inbound/Tab.tsx +1 -3
- package/src/features/tabs/adapters/inbound/Tabs.presentation.test.tsx +42 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @ankhorage/surface
|
|
2
2
|
|
|
3
|
+
## 9.1.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- ee52390: Keep inactive tab separators while removing the visible bottom border from the selected tab so active tabs visually connect to their panel without changing tab height.
|
|
8
|
+
|
|
9
|
+
## 9.1.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- a41f339: Update Ankhorage dependencies: `@ankhorage/contracts`.
|
|
14
|
+
|
|
3
15
|
## 9.1.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
# @ankhorage/surface
|
|
5
5
|
|
|
6
|
-
         
|
|
7
7
|
|
|
8
8
|
Cross-platform UI foundation for React Native and React Native Web. Provides theming, layout primitives, and core building blocks for modern apps.
|
|
9
9
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tab.d.ts","sourceRoot":"","sources":["../../../../../src/features/tabs/adapters/inbound/Tab.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAOvD,oEAAoE;AACpE,wBAAgB,GAAG,CAAC,EAClB,KAAK,EACL,QAAQ,EACR,QAAgB,EAChB,iBAA6B,EAC7B,MAAM,EACN,QAAQ,GACT,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"Tab.d.ts","sourceRoot":"","sources":["../../../../../src/features/tabs/adapters/inbound/Tab.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAOvD,oEAAoE;AACpE,wBAAgB,GAAG,CAAC,EAClB,KAAK,EACL,QAAQ,EACR,QAAgB,EAChB,iBAA6B,EAC7B,MAAM,EACN,QAAQ,GACT,EAAE,QAAQ,qBAkCV"}
|
|
@@ -15,9 +15,7 @@ export function Tab({ value, children, disabled = false, interactionPolicy = 'en
|
|
|
15
15
|
const onPress = passive || disabled ? undefined : () => setActiveValue(value);
|
|
16
16
|
return (<Pressable accessibilityLabel={undefined} accessibilityRole="tab" accessibilityState={{ disabled, selected }} aria-controls={getPanelId(value)} disabled={disabled} nativeID={getTabId(value)} onBlur={() => setFocusedValue(undefined)} onFocus={() => setFocusedValue(value)} onPress={onPress} ref={pressableRef} testID={testID}>
|
|
17
17
|
<View px="m" py="s" style={resolveTabStyle({
|
|
18
|
-
borderColor: selected
|
|
19
|
-
? theme.semantics.action.primary.base
|
|
20
|
-
: theme.semantics.border.default,
|
|
18
|
+
borderColor: selected ? 'transparent' : theme.semantics.border.default,
|
|
21
19
|
disabled,
|
|
22
20
|
})}>
|
|
23
21
|
{renderTabContent(children, trailing, selected)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tab.js","sourceRoot":"","sources":["../../../../../src/features/tabs/adapters/inbound/Tab.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAkB,MAAM,cAAc,CAAC;AAGzD,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAElE,oEAAoE;AACpE,MAAM,UAAU,GAAG,CAAC,EAClB,KAAK,EACL,QAAQ,EACR,QAAQ,GAAG,KAAK,EAChB,iBAAiB,GAAG,SAAS,EAC7B,MAAM,EACN,QAAQ,GACC;IACT,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAC;IAC7B,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,GAAG,cAAc,EAAE,CAAC;IAChG,MAAM,YAAY,GAAG,kBAAkB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,MAAM,QAAQ,GAAG,WAAW,KAAK,KAAK,CAAC;IACvC,MAAM,OAAO,GAAG,iBAAiB,KAAK,SAAS,CAAC;IAChD,MAAM,OAAO,GAAG,OAAO,IAAI,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAE9E,OAAO,CACL,CAAC,SAAS,CACR,kBAAkB,CAAC,CAAC,SAAS,CAAC,CAC9B,iBAAiB,CAAC,KAAK,CACvB,kBAAkB,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAC3C,aAAa,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CACjC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CACnB,QAAQ,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAC1B,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,CACzC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CACtC,OAAO,CAAC,CAAC,OAAO,CAAC,CACjB,GAAG,CAAC,CAAC,YAAY,CAAC,CAClB,MAAM,CAAC,CAAC,MAAM,CAAC,CAEf;MAAA,CAAC,IAAI,CACH,EAAE,CAAC,GAAG,CACN,EAAE,CAAC,GAAG,CACN,KAAK,CAAC,CAAC,eAAe,CAAC;YACrB,WAAW,EAAE,QAAQ
|
|
1
|
+
{"version":3,"file":"Tab.js","sourceRoot":"","sources":["../../../../../src/features/tabs/adapters/inbound/Tab.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\nimport { Pressable, type ViewStyle } from 'react-native';\n\nimport type { TabProps } from '../../../../types/tabs';\nimport { View } from '../../../layout/public';\nimport { useTheme } from '../../../theme/runtime';\nimport { Text } from '../../../typography/public';\nimport { useTabRegistration } from '../../composition/useTabRegistration';\nimport { useTabsContext } from '../../composition/useTabsContext';\n\n/*** Renders one accessible selectable tab inside a Tabs context. */\nexport function Tab({\n value,\n children,\n disabled = false,\n interactionPolicy = 'enabled',\n testID,\n trailing,\n}: TabProps) {\n const { theme } = useTheme();\n const { activeValue, getPanelId, getTabId, setActiveValue, setFocusedValue } = useTabsContext();\n const pressableRef = useTabRegistration({ disabled, value });\n const selected = activeValue === value;\n const passive = interactionPolicy === 'passive';\n const onPress = passive || disabled ? undefined : () => setActiveValue(value);\n\n return (\n <Pressable\n accessibilityLabel={undefined}\n accessibilityRole=\"tab\"\n accessibilityState={{ disabled, selected }}\n aria-controls={getPanelId(value)}\n disabled={disabled}\n nativeID={getTabId(value)}\n onBlur={() => setFocusedValue(undefined)}\n onFocus={() => setFocusedValue(value)}\n onPress={onPress}\n ref={pressableRef}\n testID={testID}\n >\n <View\n px=\"m\"\n py=\"s\"\n style={resolveTabStyle({\n borderColor: selected ? 'transparent' : theme.semantics.border.default,\n disabled,\n })}\n >\n {renderTabContent(children, trailing, selected)}\n </View>\n </Pressable>\n );\n}\n\n/*** Renders the canonical tab label with optional trailing content. */\nfunction renderTabContent(\n children: React.ReactNode,\n trailing: React.ReactNode,\n selected: boolean,\n): React.ReactNode {\n const label = (\n <Text color={selected ? 'primary' : undefined} variant=\"label\" weight=\"medium\">\n {children}\n </Text>\n );\n if (trailing === undefined) return label;\n return (\n <View align=\"center\" direction=\"row\" gap=\"xs\">\n {label}\n {trailing}\n </View>\n );\n}\n\n/*** Resolves active and disabled visual state without inline JSX styles. */\nfunction resolveTabStyle({\n borderColor,\n disabled,\n}: {\n borderColor: string;\n disabled: boolean;\n}): ViewStyle {\n return { borderBottomColor: borderColor, borderBottomWidth: 2, opacity: disabled ? 0.64 : 1 };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/surface",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "9.1.
|
|
4
|
+
"version": "9.1.2",
|
|
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": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@ankhorage/color-theory": "^0.3.1",
|
|
47
|
-
"@ankhorage/contracts": "^22.
|
|
47
|
+
"@ankhorage/contracts": "^22.7.0",
|
|
48
48
|
"@ankhorage/utility": "^1.5.0",
|
|
49
49
|
"@gorhom/bottom-sheet": "^5.2.13"
|
|
50
50
|
},
|
|
@@ -42,9 +42,7 @@ export function Tab({
|
|
|
42
42
|
px="m"
|
|
43
43
|
py="s"
|
|
44
44
|
style={resolveTabStyle({
|
|
45
|
-
borderColor: selected
|
|
46
|
-
? theme.semantics.action.primary.base
|
|
47
|
-
: theme.semantics.border.default,
|
|
45
|
+
borderColor: selected ? 'transparent' : theme.semantics.border.default,
|
|
48
46
|
disabled,
|
|
49
47
|
})}
|
|
50
48
|
>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { expect, mock, test } from 'bun:test';
|
|
2
|
+
import { Window } from 'happy-dom';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
5
|
+
import * as ReactNativeWeb from 'react-native-web';
|
|
6
|
+
|
|
7
|
+
await mock.module('react-native', () => ReactNativeWeb);
|
|
8
|
+
|
|
9
|
+
const { ThemeProvider } = await import('../../../theme/runtime');
|
|
10
|
+
const { Tab } = await import('./Tab');
|
|
11
|
+
const { TabList } = await import('./TabList');
|
|
12
|
+
const { Tabs } = await import('./Tabs');
|
|
13
|
+
|
|
14
|
+
test('opens the selected tab into its panel while retaining inactive separators', () => {
|
|
15
|
+
const markup = renderToStaticMarkup(
|
|
16
|
+
<ThemeProvider>
|
|
17
|
+
<Tabs defaultValue="export">
|
|
18
|
+
<TabList>
|
|
19
|
+
<Tab testID="tree-tab" value="tree">
|
|
20
|
+
Tree
|
|
21
|
+
</Tab>
|
|
22
|
+
<Tab testID="export-tab" value="export">
|
|
23
|
+
Export
|
|
24
|
+
</Tab>
|
|
25
|
+
</TabList>
|
|
26
|
+
</Tabs>
|
|
27
|
+
</ThemeProvider>,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const browserWindow = new Window();
|
|
31
|
+
browserWindow.document.body.innerHTML = markup;
|
|
32
|
+
const inactive = browserWindow.document.querySelector('[data-testid="tree-tab"] > div');
|
|
33
|
+
const active = browserWindow.document.querySelector('[data-testid="export-tab"] > div');
|
|
34
|
+
const inactiveStyle = inactive?.getAttribute('style') ?? '';
|
|
35
|
+
const activeStyle = active?.getAttribute('style') ?? '';
|
|
36
|
+
|
|
37
|
+
expect(inactiveStyle).toContain('border-bottom-width:2px');
|
|
38
|
+
expect(inactiveStyle).not.toContain('border-bottom-color:transparent');
|
|
39
|
+
expect(activeStyle).toContain('border-bottom-width:2px');
|
|
40
|
+
expect(activeStyle).toMatch(/border-bottom-color:(?:transparent|rgba\(0,0,0,0(?:\.0+)?\))/u);
|
|
41
|
+
browserWindow.close();
|
|
42
|
+
});
|