From e1af2105979ba5f14588e09bcb3192542852dbf2 Mon Sep 17 00:00:00 2001 From: Lawrence Win Date: Wed, 7 Jun 2023 10:35:17 -0700 Subject: [PATCH 01/11] Define initial tablist package Making a new PR for defining the new package. I will continuously bring code from the old tabs into here. --- .../TestComponents/TabList/TabListTest.tsx | 36 +++++ apps/fluent-tester/src/testPages.ts | 7 + packages/experimental/TabList/.eslintrc.js | 3 + packages/experimental/TabList/CHANGELOG.json | 0 packages/experimental/TabList/CHANGELOG.md | 0 packages/experimental/TabList/babel.config.js | 1 + packages/experimental/TabList/just.config.js | 3 + packages/experimental/TabList/package.json | 71 +++++++++ .../experimental/TabList/src/Tab.styling.ts | 18 +++ packages/experimental/TabList/src/Tab.tsx | 61 ++++++++ .../experimental/TabList/src/Tab.types.ts | 142 ++++++++++++++++++ .../TabList/src/TabList.styling.ts | 14 ++ packages/experimental/TabList/src/TabList.tsx | 48 ++++++ .../experimental/TabList/src/TabList.types.ts | 113 ++++++++++++++ .../TabList/src/TabListMargins.ts | 11 ++ .../experimental/TabList/src/TabListTokens.ts | 6 + .../experimental/TabList/src/TabTokens.ts | 8 + .../TabList/src/__tests__/TabList.test.tsx | 0 packages/experimental/TabList/src/index.ts | 14 ++ packages/experimental/TabList/src/useTab.ts | 15 ++ .../experimental/TabList/src/useTabList.ts | 15 ++ packages/experimental/TabList/tsconfig.json | 9 ++ 22 files changed, 595 insertions(+) create mode 100644 apps/fluent-tester/src/TestComponents/TabList/TabListTest.tsx create mode 100644 packages/experimental/TabList/.eslintrc.js create mode 100644 packages/experimental/TabList/CHANGELOG.json create mode 100644 packages/experimental/TabList/CHANGELOG.md create mode 100644 packages/experimental/TabList/babel.config.js create mode 100644 packages/experimental/TabList/just.config.js create mode 100644 packages/experimental/TabList/package.json create mode 100644 packages/experimental/TabList/src/Tab.styling.ts create mode 100644 packages/experimental/TabList/src/Tab.tsx create mode 100644 packages/experimental/TabList/src/Tab.types.ts create mode 100644 packages/experimental/TabList/src/TabList.styling.ts create mode 100644 packages/experimental/TabList/src/TabList.tsx create mode 100644 packages/experimental/TabList/src/TabList.types.ts create mode 100644 packages/experimental/TabList/src/TabListMargins.ts create mode 100644 packages/experimental/TabList/src/TabListTokens.ts create mode 100644 packages/experimental/TabList/src/TabTokens.ts create mode 100644 packages/experimental/TabList/src/__tests__/TabList.test.tsx create mode 100644 packages/experimental/TabList/src/index.ts create mode 100644 packages/experimental/TabList/src/useTab.ts create mode 100644 packages/experimental/TabList/src/useTabList.ts create mode 100644 packages/experimental/TabList/tsconfig.json diff --git a/apps/fluent-tester/src/TestComponents/TabList/TabListTest.tsx b/apps/fluent-tester/src/TestComponents/TabList/TabListTest.tsx new file mode 100644 index 0000000000..ca950c892a --- /dev/null +++ b/apps/fluent-tester/src/TestComponents/TabList/TabListTest.tsx @@ -0,0 +1,36 @@ +import React from 'react'; + +import { TabList, Tab } from '@fluentui-react-native/tablist'; + +import type { PlatformStatus, TestSection } from '../Test'; +import { Test } from '../Test'; + +const TabListMainTest: React.FunctionComponent = () => { + return ( + + Tab 1 + Tab 2 + + ); +}; + +const sections: TestSection[] = [ + { + name: 'Main Test', + component: TabListMainTest, + }, +]; + +export const TabListTest: React.FunctionComponent = () => { + const status: PlatformStatus = { + win32Status: 'Production', + uwpStatus: 'Experimental', + iosStatus: 'Backlog', + macosStatus: 'Experimental', + androidStatus: 'Backlog', + }; + + const description = 'With Tabs, users can navigate to another view.'; + + return ; +}; diff --git a/apps/fluent-tester/src/testPages.ts b/apps/fluent-tester/src/testPages.ts index 3ec2beb79b..3717205d3c 100644 --- a/apps/fluent-tester/src/testPages.ts +++ b/apps/fluent-tester/src/testPages.ts @@ -37,6 +37,7 @@ import { SpinnerTest } from './TestComponents/Spinner'; import { StrokeWidthTest } from './TestComponents/StrokeWidth'; import { SvgTest, RNSVGIconsTest } from './TestComponents/Svg'; import { SwitchTest } from './TestComponents/Switch'; +import { TabListTest } from './TestComponents/TabList/TabListTest'; import { TabsLegacyTest } from './TestComponents/TabsLegacy'; import { TabsV1Test } from './TestComponents/TabsV1'; import { TextLegacyTest } from './TestComponents/TextLegacy'; @@ -286,6 +287,12 @@ export const tests: TestDescription[] = [ testPageButton: Constants.HOMEPAGE_SWITCH_BUTTON, platforms: ['android', 'ios', 'macos', 'win32', 'windows'], }, + { + name: 'TabList', + component: TabListTest, + testPageButton: '', + platforms: ['win32', 'windows'], + }, { name: 'Tabs Legacy', component: TabsLegacyTest, diff --git a/packages/experimental/TabList/.eslintrc.js b/packages/experimental/TabList/.eslintrc.js new file mode 100644 index 0000000000..16d1d51ba4 --- /dev/null +++ b/packages/experimental/TabList/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ['@fluentui-react-native/eslint-config-rules'], +}; diff --git a/packages/experimental/TabList/CHANGELOG.json b/packages/experimental/TabList/CHANGELOG.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/experimental/TabList/CHANGELOG.md b/packages/experimental/TabList/CHANGELOG.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/experimental/TabList/babel.config.js b/packages/experimental/TabList/babel.config.js new file mode 100644 index 0000000000..e55017b160 --- /dev/null +++ b/packages/experimental/TabList/babel.config.js @@ -0,0 +1 @@ +module.exports = require('@fluentui-react-native/scripts/babel.config'); diff --git a/packages/experimental/TabList/just.config.js b/packages/experimental/TabList/just.config.js new file mode 100644 index 0000000000..4f26f8acb5 --- /dev/null +++ b/packages/experimental/TabList/just.config.js @@ -0,0 +1,3 @@ +const { preset } = require('@fluentui-react-native/scripts'); + +preset(); diff --git a/packages/experimental/TabList/package.json b/packages/experimental/TabList/package.json new file mode 100644 index 0000000000..222686b749 --- /dev/null +++ b/packages/experimental/TabList/package.json @@ -0,0 +1,71 @@ +{ + "name": "@fluentui-react-native/tablist", + "version": "0.1.0", + "description": "A cross-platform TabList component using the Fluent Design System", + "main": "src/index.ts", + "module": "src/index.ts", + "typings": "lib/index.d.ts", + "onPublish": { + "main": "lib-commonjs/index.js", + "module": "lib/index.js" + }, + "scripts": { + "build": "fluentui-scripts build", + "clean": "fluentui-scripts clean", + "depcheck": "fluentui-scripts depcheck", + "just": "fluentui-scripts", + "lint": "fluentui-scripts eslint", + "test": "fluentui-scripts jest", + "update-snapshots": "fluentui-scripts jest -u", + "prettier": "fluentui-scripts prettier", + "prettier-fix": "fluentui-scripts prettier --fix true" + }, + "repository": { + "type": "git", + "url": "https://github.com/microsoft/fluentui-react-native.git", + "directory": "packages/experimental/TabList" + }, + "dependencies": { + "@fluentui-react-native/framework": "0.9.8", + "@fluentui-react-native/interactive-hooks": ">=0.22.29 <1.0.0", + "@fluentui-react-native/focus-zone": ">=0.11.41 <1.0.0", + "@fluentui-react-native/text": ">=0.19.31 <1.0.0", + "@fluentui-react-native/tokens": ">=0.20.14 <1.0.0", + "@fluentui-react-native/use-styling": ">=0.9.4 <1.0.0", + "@fluentui-react-native/icon": "0.17.25", + "@fluentui-react-native/adapters": "0.10.2", + "tslib": "^2.3.1" + }, + "devDependencies": { + "@fluentui-react-native/eslint-config-rules": "^0.1.1", + "@fluentui-react-native/scripts": "^0.1.1", + "@fluentui-react-native/test-tools": ">=0.1.1 <1.0.0", + "@office-iss/react-native-win32": "^0.68.0", + "@types/react-native": "^0.68.0", + "react": "17.0.2", + "react-native": "^0.68.0" + }, + "peerDependencies": { + "react": "17.0.2", + "react-native": "^0.68.0" + }, + "author": "", + "license": "MIT", + "rnx-kit": { + "kitType": "library", + "alignDeps": { + "presets": [ + "microsoft/react-native" + ], + "requirements": [ + "react-native@0.68" + ], + "capabilities": [ + "core", + "core-android", + "core-ios", + "react" + ] + } + } +} diff --git a/packages/experimental/TabList/src/Tab.styling.ts b/packages/experimental/TabList/src/Tab.styling.ts new file mode 100644 index 0000000000..4344dc7e8e --- /dev/null +++ b/packages/experimental/TabList/src/Tab.styling.ts @@ -0,0 +1,18 @@ +import type { UseStylingOptions } from '@fluentui-react-native/framework'; +import { buildProps } from '@fluentui-react-native/framework'; + +import { tabName } from './Tab.types'; +import type { TabSlotProps, TabTokens, TabProps } from './Tab.types'; +import { tabStates, defaultTabTokens } from './TabTokens'; + +export const stylingSettings: UseStylingOptions = { + tokens: [defaultTabTokens, tabName], + states: tabStates, + slotProps: { + root: buildProps(() => ({}), []), + content: buildProps(() => ({}), []), + icon: buildProps(() => ({}), []), + stack: buildProps(() => ({}), []), + indicator: buildProps(() => ({}), []), + }, +}; diff --git a/packages/experimental/TabList/src/Tab.tsx b/packages/experimental/TabList/src/Tab.tsx new file mode 100644 index 0000000000..f27b02db8c --- /dev/null +++ b/packages/experimental/TabList/src/Tab.tsx @@ -0,0 +1,61 @@ +/** @jsx withSlots */ +import * as React from 'react'; +import { Pressable, View } from 'react-native'; + +import type { UseSlots } from '@fluentui-react-native/framework'; +import { compose, mergeProps, withSlots } from '@fluentui-react-native/framework'; +import { Icon, createIconProps } from '@fluentui-react-native/icon'; +import { TextV1 as Text } from '@fluentui-react-native/text'; + +import { stylingSettings } from './Tab.styling'; +import type { TabType, TabProps } from './Tab.types'; +import { tabName } from './Tab.types'; +import { useTab } from './useTab'; + +export const Tab = compose({ + displayName: tabName, + ...stylingSettings, + slots: { + root: Pressable, + stack: View, + icon: Icon, + indicator: View, + content: Text, + }, + useRender: (userProps: TabProps, useSlots: UseSlots) => { + const tabsItem = useTab(userProps); + + const iconProps = createIconProps(userProps.icon); + + // Grab the styled slots. + const Slots = useSlots(userProps, (layer) => tabsItem.state[layer] || userProps[layer]); + + // Return the handler to finish render. + return (final: TabProps, ...children: React.ReactNode[]) => { + if (!tabsItem.state) { + return null; + } + + const { icon, itemKey, itemCount, headerText, ...mergedProps } = mergeProps(tabsItem.props, final); + + let containerText: string | null = null; + React.Children.forEach(children, (child) => { + if (typeof child === 'string') { + containerText = child; + } + }); + + return ( + + + {icon && } + {containerText} + + + + ); + }; + }, +}); + +export default Tab; diff --git a/packages/experimental/TabList/src/Tab.types.ts b/packages/experimental/TabList/src/Tab.types.ts new file mode 100644 index 0000000000..d1dc57125f --- /dev/null +++ b/packages/experimental/TabList/src/Tab.types.ts @@ -0,0 +1,142 @@ +import type * as React from 'react'; +import type { ViewStyle, ColorValue } from 'react-native'; + +import type { IViewProps } from '@fluentui-react-native/adapters'; +import type { IconPropsV1 as IconProps } from '@fluentui-react-native/icon'; +import type { IFocusable, PressableState, PressablePropsExtended } from '@fluentui-react-native/interactive-hooks'; +import type { TextProps } from '@fluentui-react-native/text'; +import type { FontTokens, IBorderTokens } from '@fluentui-react-native/tokens'; + +export const tabName = 'Tab'; + +export interface TabTokens extends FontTokens, IBorderTokens { + /** + * The indicator color. + */ + indicatorColor?: string; + + /** + * The opacity of the tabs item. + */ + tabsItemOpacity?: number; + + /** + * The indicator marginHorizontal value. + */ + indicatorMarginHorizontal?: number; + + /** + * The icon color. + */ + iconColor?: string; + + /** + * The amount of padding between the border and the headerText. + */ + headerTextPadding?: number | string; + + /** + * The amount of padding between the border and the headerText when the TabsItem has focus. + */ + headerTextPaddingFocused?: number | string; + /** + * Background color for the button + */ + backgroundColor?: ColorValue; + + /** + * Foreground color for the text and/or icon of the button + */ + color?: ColorValue; + + /** + * The amount of padding between the border and the contents. + */ + contentPadding?: number | string; + + /** + * The amount of padding between the border and the contents when the Button has focus. + */ + contentPaddingFocused?: number | string; + + /** + * The icon color when hovering over the Button. + */ + iconColorHovered?: ColorValue; + + /** + * The icon color when the Button is being pressed. + */ + iconColorPressed?: ColorValue; + + /** + * The size of the icon. + */ + iconSize?: number | string; + + /** + * The weight of the lines used when drawing the icon. + */ + iconWeight?: number; + + width?: ViewStyle['width']; + minHeight?: ViewStyle['minHeight']; + minWidth?: ViewStyle['minWidth']; + + /** + * States that can be applied to a button + */ + hovered?: TabTokens; + focused?: TabTokens; + pressed?: TabTokens; + disabled?: TabTokens; + selected?: TabTokens; +} + +export interface TabProps extends Omit { + /** + * A unique key-identifier for each option + */ + value: unknown; + + /** + * Whether or not the tabs item is selectable + */ + disabled?: boolean; + + /** + * Source URL or name of the icon to show on the Button. + */ + icon?: IconProps; + + /** + * A RefObject to access the IButton interface. Use this to access the public methods and properties of the component. + */ + componentRef?: React.RefObject; + + testID?: string; +} + +export interface TabState extends PressableState { + selected?: boolean; +} + +export interface TabInfo { + props: TabProps & React.ComponentPropsWithRef; + state: TabState; +} + +export interface TabSlotProps { + root: React.PropsWithRef; + icon: IconProps; + stack: IViewProps; + indicator: IViewProps; + content: TextProps; +} + +export interface TabType { + props: TabProps; + tokens: TabTokens; + slotProps: TabSlotProps; + state: TabState; +} diff --git a/packages/experimental/TabList/src/TabList.styling.ts b/packages/experimental/TabList/src/TabList.styling.ts new file mode 100644 index 0000000000..eb68d1dd44 --- /dev/null +++ b/packages/experimental/TabList/src/TabList.styling.ts @@ -0,0 +1,14 @@ +import type { UseStylingOptions } from '@fluentui-react-native/framework'; +import { buildProps } from '@fluentui-react-native/framework'; + +import { tabListName } from './TabList.types'; +import type { TabListTokens, TabListSlotProps, TabListProps } from './TabList.types'; +import { defaultTabListTokens } from './TabListTokens'; + +export const stylingSettings: UseStylingOptions = { + tokens: [defaultTabListTokens, tabListName], + slotProps: { + root: buildProps(() => ({}), []), + stack: buildProps(() => ({}), []), + }, +}; diff --git a/packages/experimental/TabList/src/TabList.tsx b/packages/experimental/TabList/src/TabList.tsx new file mode 100644 index 0000000000..d7a98b1c65 --- /dev/null +++ b/packages/experimental/TabList/src/TabList.tsx @@ -0,0 +1,48 @@ +/** @jsx withSlots */ +import * as React from 'react'; +import { Pressable, View } from 'react-native'; + +import { FocusZone } from '@fluentui-react-native/focus-zone'; +import type { UseSlots } from '@fluentui-react-native/framework'; +import { compose, withSlots } from '@fluentui-react-native/framework'; + +import type { TabProps } from './Tab.types'; +import { stylingSettings } from './TabList.styling'; +import type { TabListType, TabListProps, TabListContextData } from './TabList.types'; +import { tabListName } from './TabList.types'; +import { useTabList } from './useTabList'; + +export const TabListContext = React.createContext({ + selectedValue: null, + onTabSelect: () => { + return; + }, + updateSelectedTabsItemRef: () => { + return; + }, + tabValues: [], +}); + +export const TabList = compose({ + displayName: tabListName, + ...stylingSettings, + slots: { + root: Pressable, + container: FocusZone, + stack: View, + }, + useRender: (userProps: TabListProps, useSlots: UseSlots) => { + // configure props and state for tabs based on user props + const tabs = useTabList(userProps); + + // Grab the styled slots. + const Slots = useSlots(userProps, (layer) => tabs.state[layer] || userProps[layer]); + + // Return the handler to finish render. + return () => { + return ; + }; + }, +}); + +export default TabList; diff --git a/packages/experimental/TabList/src/TabList.types.ts b/packages/experimental/TabList/src/TabList.types.ts new file mode 100644 index 0000000000..a4d1dbf58c --- /dev/null +++ b/packages/experimental/TabList/src/TabList.types.ts @@ -0,0 +1,113 @@ +import type * as React from 'react'; +import type { View } from 'react-native'; + +import type { IViewProps } from '@fluentui-react-native/adapters'; +import type { FocusZoneProps } from '@fluentui-react-native/focus-zone'; +import type { FontTokens, IForegroundColorTokens, IBackgroundColorTokens } from '@fluentui-react-native/tokens'; + +export const tabListName = 'TabList'; + +export interface TabListContextData { + /** + * The currently selected TabsItem's key + */ + selectedValue: unknown; + + /** + * Updates the selected tabsItem and calls the client’s onTabsClick callback + */ + onTabSelect?: (value: unknown) => void; + + /** + * Updates the selected tabsItem's ref to set as the default tabbable element + */ + updateSelectedTabsItemRef?: (ref: React.RefObject) => void; + + /** + * Array of tabsItem keys in the group + */ + tabValues?: unknown[]; + + /** + * Reference to the Focus Container as there is no FocusZone on windows. + * GH #964 + */ + focusZoneRef?: React.RefObject | null; +} + +export interface TabListTokens extends IForegroundColorTokens, FontTokens, IBackgroundColorTokens {} + +export type TabListAppearance = 'transparent' | 'subtle'; +export type TabListSize = 'small' | 'medium' | 'large'; + +export interface TabListProps extends Pick, IViewProps { + /** + * Visual appearance of the TabList, affecting header hover / selection background. + */ + appearance?: TabListAppearance; + + /** + * The key of the TabsItem that will initially be selected + */ + defaultSelectedValue?: unknown; + + /** + * Flag to disable all tabs + */ + disabled?: boolean; + + /** + * Callback for receiving a notification when the choice has been changed + */ + onTabSelect?: (value: unknown) => void; + + /** + * The value of the selected option. If you provide this, you must maintain selection state by observing + * onTabsClick events and passing a new value in when changed. This overrides defaultSelectedValue + * and makes the Tabs a controlled component. This prop is mutually exclusive to defaultSelectedValue. + */ + selectedValue?: unknown; + + /** + * Flag to change the size of the tabs. + */ + size?: TabListSize; + + /** + * Flag to render the list of tabs horizontally or vertically + */ + vertical?: boolean; + + /** + * A RefObject to access Tabs. + */ + componentRef?: React.RefObject; + + testID?: string; +} + +export interface TabListState { + context?: TabListContextData; + + /** + * Array of enabled keys in the group + * Windows-Specific Prop. + */ + enabledValues?: unknown[]; +} +export interface TabListInfo { + props: TabListProps; + state: TabListState; +} +export interface TabListSlotProps { + root: React.PropsWithRef; + container?: FocusZoneProps; + stack: IViewProps; +} + +export interface TabListType { + props: TabListProps; + tokens: TabListTokens; + slotProps: TabListSlotProps; + state: TabListState; +} diff --git a/packages/experimental/TabList/src/TabListMargins.ts b/packages/experimental/TabList/src/TabListMargins.ts new file mode 100644 index 0000000000..59998b697d --- /dev/null +++ b/packages/experimental/TabList/src/TabListMargins.ts @@ -0,0 +1,11 @@ +export const getRootMargins = () => { + return {}; +}; + +export const getLabelMargins = () => { + return {}; +}; + +export const getStackMargins = () => { + return {}; +}; diff --git a/packages/experimental/TabList/src/TabListTokens.ts b/packages/experimental/TabList/src/TabListTokens.ts new file mode 100644 index 0000000000..f093b6c1ed --- /dev/null +++ b/packages/experimental/TabList/src/TabListTokens.ts @@ -0,0 +1,6 @@ +import type { Theme } from '@fluentui-react-native/framework'; +import type { TokenSettings } from '@fluentui-react-native/use-styling'; + +import type { TabListTokens } from '.'; + +export const defaultTabListTokens: TokenSettings = () => ({} as TabListTokens); diff --git a/packages/experimental/TabList/src/TabTokens.ts b/packages/experimental/TabList/src/TabTokens.ts new file mode 100644 index 0000000000..8a785a37e6 --- /dev/null +++ b/packages/experimental/TabList/src/TabTokens.ts @@ -0,0 +1,8 @@ +import type { Theme } from '@fluentui-react-native/framework'; +import type { TokenSettings } from '@fluentui-react-native/use-styling'; + +import type { TabTokens } from '.'; + +export const tabStates: (keyof TabTokens)[] = ['hovered', 'selected', 'focused', 'disabled', 'pressed']; + +export const defaultTabTokens: TokenSettings = () => ({} as TabTokens); diff --git a/packages/experimental/TabList/src/__tests__/TabList.test.tsx b/packages/experimental/TabList/src/__tests__/TabList.test.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/experimental/TabList/src/index.ts b/packages/experimental/TabList/src/index.ts new file mode 100644 index 0000000000..5ccb1debb9 --- /dev/null +++ b/packages/experimental/TabList/src/index.ts @@ -0,0 +1,14 @@ +export { tabName } from './Tab.types'; +export type { TabType, TabInfo, TabProps, TabSlotProps, TabState, TabTokens } from './Tab.types'; +export { Tab } from './Tab'; +export { tabListName } from './TabList.types'; +export type { + TabListContextData, + TabListInfo, + TabListProps, + TabListSlotProps, + TabListState, + TabListTokens, + TabListType, +} from './TabList.types'; +export { TabList, TabListContext } from './TabList'; diff --git a/packages/experimental/TabList/src/useTab.ts b/packages/experimental/TabList/src/useTab.ts new file mode 100644 index 0000000000..743c621f9d --- /dev/null +++ b/packages/experimental/TabList/src/useTab.ts @@ -0,0 +1,15 @@ +import type { TabProps, TabInfo } from './Tab.types'; + +/** + * Re-usable hook for TabsItem. + * This hook configures tabs item props and state for TabsItem. + * + * @param props user props sent to TabsItem + * @returns configured props and state for TabsItem + */ +export const useTab = (props: TabProps): TabInfo => { + return { + props: props, + state: {}, + }; +}; diff --git a/packages/experimental/TabList/src/useTabList.ts b/packages/experimental/TabList/src/useTabList.ts new file mode 100644 index 0000000000..abef91e9d1 --- /dev/null +++ b/packages/experimental/TabList/src/useTabList.ts @@ -0,0 +1,15 @@ +import type { TabListProps, TabListInfo } from './TabList.types'; + +/** + * Re-usable hook for Tabs. + * This hook configures tabs props and state for Tabs. + * + * @param props user props sent to Tabs + * @returns configured props and state for Tabs + */ +export const useTabList = (props: TabListProps): TabListInfo => { + return { + props: props, + state: {}, + }; +}; diff --git a/packages/experimental/TabList/tsconfig.json b/packages/experimental/TabList/tsconfig.json new file mode 100644 index 0000000000..65d97d1a91 --- /dev/null +++ b/packages/experimental/TabList/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@fluentui-react-native/scripts/tsconfig.json", + "compilerOptions": { + "importHelpers": true, + "outDir": "lib", + "types": ["node", "jest"] + }, + "include": ["src"] +} From 3e235c78f70049fbbd70dab03a4744e7fb8cc649 Mon Sep 17 00:00:00 2001 From: Lawrence Win Date: Wed, 7 Jun 2023 10:45:47 -0700 Subject: [PATCH 02/11] Change files --- ...ative-tablist-d5d9df1a-961f-40d7-8941-e3aa24ca852a.json | 7 +++++++ ...native-tester-7cd30a3b-a873-4d90-8a1a-7902f05aa58e.json | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 change/@fluentui-react-native-tablist-d5d9df1a-961f-40d7-8941-e3aa24ca852a.json create mode 100644 change/@fluentui-react-native-tester-7cd30a3b-a873-4d90-8a1a-7902f05aa58e.json diff --git a/change/@fluentui-react-native-tablist-d5d9df1a-961f-40d7-8941-e3aa24ca852a.json b/change/@fluentui-react-native-tablist-d5d9df1a-961f-40d7-8941-e3aa24ca852a.json new file mode 100644 index 0000000000..e1110b3204 --- /dev/null +++ b/change/@fluentui-react-native-tablist-d5d9df1a-961f-40d7-8941-e3aa24ca852a.json @@ -0,0 +1,7 @@ +{ + "type": "none", + "comment": "Define initial tablist package", + "packageName": "@fluentui-react-native/tablist", + "email": "winlarry@microsoft.com", + "dependentChangeType": "none" +} diff --git a/change/@fluentui-react-native-tester-7cd30a3b-a873-4d90-8a1a-7902f05aa58e.json b/change/@fluentui-react-native-tester-7cd30a3b-a873-4d90-8a1a-7902f05aa58e.json new file mode 100644 index 0000000000..b563638d0e --- /dev/null +++ b/change/@fluentui-react-native-tester-7cd30a3b-a873-4d90-8a1a-7902f05aa58e.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Add TabList test page on win32", + "packageName": "@fluentui-react-native/tester", + "email": "winlarry@microsoft.com", + "dependentChangeType": "patch" +} From 3fdf3c966121188fc245481e86f37f58fc514a43 Mon Sep 17 00:00:00 2001 From: Lawrence Win Date: Wed, 7 Jun 2023 11:13:26 -0700 Subject: [PATCH 03/11] Fix lint errors --- packages/experimental/TabList/src/TabList.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/experimental/TabList/src/TabList.tsx b/packages/experimental/TabList/src/TabList.tsx index d7a98b1c65..1a826a8c90 100644 --- a/packages/experimental/TabList/src/TabList.tsx +++ b/packages/experimental/TabList/src/TabList.tsx @@ -6,7 +6,6 @@ import { FocusZone } from '@fluentui-react-native/focus-zone'; import type { UseSlots } from '@fluentui-react-native/framework'; import { compose, withSlots } from '@fluentui-react-native/framework'; -import type { TabProps } from './Tab.types'; import { stylingSettings } from './TabList.styling'; import type { TabListType, TabListProps, TabListContextData } from './TabList.types'; import { tabListName } from './TabList.types'; From 6c68b0c520f394cc16b8ab663958ea4999bca012 Mon Sep 17 00:00:00 2001 From: Lawrence Win Date: Thu, 8 Jun 2023 16:10:31 -0700 Subject: [PATCH 04/11] Bump dependencies --- packages/experimental/TabList/package.json | 23 +++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/experimental/TabList/package.json b/packages/experimental/TabList/package.json index 222686b749..149a44089a 100644 --- a/packages/experimental/TabList/package.json +++ b/packages/experimental/TabList/package.json @@ -26,24 +26,23 @@ "directory": "packages/experimental/TabList" }, "dependencies": { - "@fluentui-react-native/framework": "0.9.8", - "@fluentui-react-native/interactive-hooks": ">=0.22.29 <1.0.0", - "@fluentui-react-native/focus-zone": ">=0.11.41 <1.0.0", - "@fluentui-react-native/text": ">=0.19.31 <1.0.0", - "@fluentui-react-native/tokens": ">=0.20.14 <1.0.0", - "@fluentui-react-native/use-styling": ">=0.9.4 <1.0.0", - "@fluentui-react-native/icon": "0.17.25", - "@fluentui-react-native/adapters": "0.10.2", + "@fluentui-react-native/framework": "0.10.0", + "@fluentui-react-native/interactive-hooks": ">=0.23.0 <1.0.0", + "@fluentui-react-native/focus-zone": ">=0.12.0 <1.0.0", + "@fluentui-react-native/text": ">=0.20.0 <1.0.0", + "@fluentui-react-native/tokens": ">=0.21.0 <1.0.0", + "@fluentui-react-native/use-styling": ">=0.10.0 <1.0.0", + "@fluentui-react-native/icon": "0.18.0", + "@fluentui-react-native/adapters": "0.11.0", "tslib": "^2.3.1" }, "devDependencies": { "@fluentui-react-native/eslint-config-rules": "^0.1.1", "@fluentui-react-native/scripts": "^0.1.1", "@fluentui-react-native/test-tools": ">=0.1.1 <1.0.0", - "@office-iss/react-native-win32": "^0.68.0", - "@types/react-native": "^0.68.0", - "react": "17.0.2", - "react-native": "^0.68.0" + "@office-iss/react-native-win32": "^0.71.0", + "react": "18.2.0", + "react-native": "^0.71.0" }, "peerDependencies": { "react": "17.0.2", From 58e60f6d412ce4bb7e5e80411d9c3ef66c36d2cb Mon Sep 17 00:00:00 2001 From: Lawrence Win Date: Thu, 8 Jun 2023 16:43:41 -0700 Subject: [PATCH 05/11] More fixes --- packages/experimental/TabList/package.json | 6 ++-- packages/experimental/TabList/src/Tab.tsx | 33 +++---------------- packages/experimental/TabList/src/TabList.tsx | 1 + 3 files changed, 9 insertions(+), 31 deletions(-) diff --git a/packages/experimental/TabList/package.json b/packages/experimental/TabList/package.json index 149a44089a..6aa9953e5b 100644 --- a/packages/experimental/TabList/package.json +++ b/packages/experimental/TabList/package.json @@ -45,8 +45,8 @@ "react-native": "^0.71.0" }, "peerDependencies": { - "react": "17.0.2", - "react-native": "^0.68.0" + "react": "18.2.0", + "react-native": "^0.71.0" }, "author": "", "license": "MIT", @@ -57,7 +57,7 @@ "microsoft/react-native" ], "requirements": [ - "react-native@0.68" + "react-native@0.71" ], "capabilities": [ "core", diff --git a/packages/experimental/TabList/src/Tab.tsx b/packages/experimental/TabList/src/Tab.tsx index f27b02db8c..5f065c0396 100644 --- a/packages/experimental/TabList/src/Tab.tsx +++ b/packages/experimental/TabList/src/Tab.tsx @@ -1,10 +1,10 @@ +/** @jsxRuntime classic */ /** @jsx withSlots */ -import * as React from 'react'; import { Pressable, View } from 'react-native'; import type { UseSlots } from '@fluentui-react-native/framework'; -import { compose, mergeProps, withSlots } from '@fluentui-react-native/framework'; -import { Icon, createIconProps } from '@fluentui-react-native/icon'; +import { compose, withSlots } from '@fluentui-react-native/framework'; +import { Icon } from '@fluentui-react-native/icon'; import { TextV1 as Text } from '@fluentui-react-native/text'; import { stylingSettings } from './Tab.styling'; @@ -25,35 +25,12 @@ export const Tab = compose({ useRender: (userProps: TabProps, useSlots: UseSlots) => { const tabsItem = useTab(userProps); - const iconProps = createIconProps(userProps.icon); - // Grab the styled slots. const Slots = useSlots(userProps, (layer) => tabsItem.state[layer] || userProps[layer]); // Return the handler to finish render. - return (final: TabProps, ...children: React.ReactNode[]) => { - if (!tabsItem.state) { - return null; - } - - const { icon, itemKey, itemCount, headerText, ...mergedProps } = mergeProps(tabsItem.props, final); - - let containerText: string | null = null; - React.Children.forEach(children, (child) => { - if (typeof child === 'string') { - containerText = child; - } - }); - - return ( - - - {icon && } - {containerText} - - - - ); + return () => { + return ; }; }, }); diff --git a/packages/experimental/TabList/src/TabList.tsx b/packages/experimental/TabList/src/TabList.tsx index 1a826a8c90..0d2384d0d8 100644 --- a/packages/experimental/TabList/src/TabList.tsx +++ b/packages/experimental/TabList/src/TabList.tsx @@ -1,3 +1,4 @@ +/** @jsxRuntime classic */ /** @jsx withSlots */ import * as React from 'react'; import { Pressable, View } from 'react-native'; From 3498a923556d9ca60ca66cc680a43f1ee75036e0 Mon Sep 17 00:00:00 2001 From: Lawrence Win Date: Thu, 8 Jun 2023 21:21:58 -0700 Subject: [PATCH 06/11] Add missing fluent tester dependency --- apps/fluent-tester/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/fluent-tester/package.json b/apps/fluent-tester/package.json index 0484bd9165..a33618184a 100644 --- a/apps/fluent-tester/package.json +++ b/apps/fluent-tester/package.json @@ -70,6 +70,7 @@ "@fluentui-react-native/spinner": "^0.5.0", "@fluentui-react-native/stack": ">=0.8.0 <1.0.0", "@fluentui-react-native/switch": "^0.9.0", + "@fluentui-react-native/tablist": "0.1.0", "@fluentui-react-native/text": ">=0.20.0 <1.0.0", "@fluentui-react-native/theme": ">=0.9.0 <1.0.0", "@fluentui-react-native/theme-tokens": "^0.25.0", From 67c6f65c813311a52185450dbd3b3095bd298b92 Mon Sep 17 00:00:00 2001 From: Lawrence Win Date: Mon, 12 Jun 2023 09:41:30 -0700 Subject: [PATCH 07/11] Remove tablist margins file --- packages/experimental/TabList/src/TabListMargins.ts | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 packages/experimental/TabList/src/TabListMargins.ts diff --git a/packages/experimental/TabList/src/TabListMargins.ts b/packages/experimental/TabList/src/TabListMargins.ts deleted file mode 100644 index 59998b697d..0000000000 --- a/packages/experimental/TabList/src/TabListMargins.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const getRootMargins = () => { - return {}; -}; - -export const getLabelMargins = () => { - return {}; -}; - -export const getStackMargins = () => { - return {}; -}; From 5274355a8d61cb3cb552462f8e8fc6f5b85f118a Mon Sep 17 00:00:00 2001 From: Lawrence Win Date: Tue, 13 Jun 2023 13:50:12 -0700 Subject: [PATCH 08/11] Change platform status to 'Experimental' --- apps/fluent-tester/src/TestComponents/TabList/TabListTest.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/fluent-tester/src/TestComponents/TabList/TabListTest.tsx b/apps/fluent-tester/src/TestComponents/TabList/TabListTest.tsx index ca950c892a..2e7db4d63a 100644 --- a/apps/fluent-tester/src/TestComponents/TabList/TabListTest.tsx +++ b/apps/fluent-tester/src/TestComponents/TabList/TabListTest.tsx @@ -23,7 +23,7 @@ const sections: TestSection[] = [ export const TabListTest: React.FunctionComponent = () => { const status: PlatformStatus = { - win32Status: 'Production', + win32Status: 'Experimental', uwpStatus: 'Experimental', iosStatus: 'Backlog', macosStatus: 'Experimental', From 1ada91b414d2a44cf02ed96b9b98f30ec7e2a032 Mon Sep 17 00:00:00 2001 From: Lawrence Win Date: Wed, 14 Jun 2023 15:26:15 -0700 Subject: [PATCH 09/11] Change use of value to key string --- .../experimental/TabList/src/Tab.types.ts | 6 ++-- .../experimental/TabList/src/TabList.types.ts | 30 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/experimental/TabList/src/Tab.types.ts b/packages/experimental/TabList/src/Tab.types.ts index d1dc57125f..ebe385ab5c 100644 --- a/packages/experimental/TabList/src/Tab.types.ts +++ b/packages/experimental/TabList/src/Tab.types.ts @@ -16,7 +16,7 @@ export interface TabTokens extends FontTokens, IBorderTokens { indicatorColor?: string; /** - * The opacity of the tabs item. + * The opacity of the Tab. */ tabsItemOpacity?: number; @@ -97,10 +97,10 @@ export interface TabProps extends Omit { /** * A unique key-identifier for each option */ - value: unknown; + key: string; /** - * Whether or not the tabs item is selectable + * Whether or not the tab is selectable */ disabled?: boolean; diff --git a/packages/experimental/TabList/src/TabList.types.ts b/packages/experimental/TabList/src/TabList.types.ts index a4d1dbf58c..2530846f8d 100644 --- a/packages/experimental/TabList/src/TabList.types.ts +++ b/packages/experimental/TabList/src/TabList.types.ts @@ -9,24 +9,24 @@ export const tabListName = 'TabList'; export interface TabListContextData { /** - * The currently selected TabsItem's key + * The currently selected Tab's key */ - selectedValue: unknown; + selectedKey: string; /** - * Updates the selected tabsItem and calls the client’s onTabsClick callback + * Updates the selected Tab and calls the client’s onTabSelect callback */ - onTabSelect?: (value: unknown) => void; + onTabSelect?: (key: string) => void; /** - * Updates the selected tabsItem's ref to set as the default tabbable element + * Updates the selected Tab's ref to set as the default tabbable element */ updateSelectedTabsItemRef?: (ref: React.RefObject) => void; /** - * Array of tabsItem keys in the group + * Array of Tab values in the group */ - tabValues?: unknown[]; + tabKeys?: string[]; /** * Reference to the Focus Container as there is no FocusZone on windows. @@ -47,9 +47,9 @@ export interface TabListProps extends Pick void; + onTabSelect?: (key: string) => void; /** * The value of the selected option. If you provide this, you must maintain selection state by observing - * onTabsClick events and passing a new value in when changed. This overrides defaultSelectedValue - * and makes the Tabs a controlled component. This prop is mutually exclusive to defaultSelectedValue. + * onTabSelect events and passing a new value in when changed. This overrides defaultSelectedKey + * and makes the TabList a controlled component. This prop is mutually exclusive to defaultSelectedKey. */ - selectedValue?: unknown; + selectedKey?: string; /** * Flag to change the size of the tabs. @@ -79,7 +79,7 @@ export interface TabListProps extends Pick; @@ -93,7 +93,7 @@ export interface TabListState { * Array of enabled keys in the group * Windows-Specific Prop. */ - enabledValues?: unknown[]; + enabledKeys?: string[]; } export interface TabListInfo { props: TabListProps; From c00bbdb639efbcb73a8e8c677be0842c039a516f Mon Sep 17 00:00:00 2001 From: Lawrence Win Date: Wed, 14 Jun 2023 16:06:15 -0700 Subject: [PATCH 10/11] Fix type error --- packages/experimental/TabList/src/TabList.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/experimental/TabList/src/TabList.tsx b/packages/experimental/TabList/src/TabList.tsx index 0d2384d0d8..bbd533cd51 100644 --- a/packages/experimental/TabList/src/TabList.tsx +++ b/packages/experimental/TabList/src/TabList.tsx @@ -13,14 +13,14 @@ import { tabListName } from './TabList.types'; import { useTabList } from './useTabList'; export const TabListContext = React.createContext({ - selectedValue: null, + selectedKey: '', onTabSelect: () => { return; }, updateSelectedTabsItemRef: () => { return; }, - tabValues: [], + tabKeys: [], }); export const TabList = compose({ From 8087df2aba6ccd1c4def2044a60a9a970a7438d8 Mon Sep 17 00:00:00 2001 From: Lawrence Win Date: Wed, 14 Jun 2023 16:40:19 -0700 Subject: [PATCH 11/11] Fix value -> key on test page --- apps/fluent-tester/src/TestComponents/TabList/TabListTest.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/fluent-tester/src/TestComponents/TabList/TabListTest.tsx b/apps/fluent-tester/src/TestComponents/TabList/TabListTest.tsx index 2e7db4d63a..aaa2d8e0ea 100644 --- a/apps/fluent-tester/src/TestComponents/TabList/TabListTest.tsx +++ b/apps/fluent-tester/src/TestComponents/TabList/TabListTest.tsx @@ -8,8 +8,8 @@ import { Test } from '../Test'; const TabListMainTest: React.FunctionComponent = () => { return ( - Tab 1 - Tab 2 + Tab 1 + Tab 2 ); };