@khanacademy/wonder-blocks-tabs 0.0.0-PR2913-20251217210125 → 0.0.0-PR2913-20251218223804
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,4 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import { TabsProps } from "./tabs";
|
|
3
|
+
import { TabsDropdownProps } from "./tabs-dropdown";
|
|
2
4
|
import { AriaLabelOrAriaLabelledby } from "./types";
|
|
3
5
|
export type ResponsiveTabItem = {
|
|
4
6
|
/**
|
|
@@ -13,8 +15,32 @@ export type ResponsiveTabItem = {
|
|
|
13
15
|
* The panel of the tab.
|
|
14
16
|
*/
|
|
15
17
|
panel: React.ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* Optional test ID for e2e testing of the tab.
|
|
20
|
+
*/
|
|
21
|
+
testId?: string;
|
|
16
22
|
};
|
|
17
23
|
type Props = AriaLabelOrAriaLabelledby & {
|
|
24
|
+
/**
|
|
25
|
+
* A unique id for the component.
|
|
26
|
+
*
|
|
27
|
+
* Here is how the id is used for the different elements in the component:
|
|
28
|
+
* - The root will have an id of `${id}`
|
|
29
|
+
*
|
|
30
|
+
* To set the id of the tabs or dropdown, set the `id` prop in the props:
|
|
31
|
+
* `tabsProps` or `dropdownProps`.
|
|
32
|
+
*/
|
|
33
|
+
id?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Optional test ID for e2e testing.
|
|
36
|
+
*
|
|
37
|
+
* Here is how the test id is used for the different elements in the component:
|
|
38
|
+
* - The root will have a testId of `${testId}`
|
|
39
|
+
*
|
|
40
|
+
* To set the test id of the tabs or dropdown, set the `testId` prop in the props:
|
|
41
|
+
* `tabsProps` or `dropdownProps`.
|
|
42
|
+
*/
|
|
43
|
+
testId?: string;
|
|
18
44
|
/**
|
|
19
45
|
* The tabs to render.
|
|
20
46
|
*/
|
|
@@ -31,6 +57,20 @@ type Props = AriaLabelOrAriaLabelledby & {
|
|
|
31
57
|
* Called when the layout changes.
|
|
32
58
|
*/
|
|
33
59
|
onLayoutChange?: (layout: "tabs" | "dropdown") => void;
|
|
60
|
+
/**
|
|
61
|
+
* Additional props to pass to the Tabs component when it is used.
|
|
62
|
+
*
|
|
63
|
+
* Note: This prop doesn't include the props that are available on the
|
|
64
|
+
* ResponsiveTabs component already.
|
|
65
|
+
*/
|
|
66
|
+
tabsProps?: Omit<TabsProps, "tabs" | "selectedTabId" | "onTabSelected" | "aria-label" | "aria-labelledby">;
|
|
67
|
+
/**
|
|
68
|
+
* Additional props to pass to the TabsDropdown component when it is used.
|
|
69
|
+
*
|
|
70
|
+
* Note: This prop doesn't include the props that are available on the
|
|
71
|
+
* ResponsiveTabs component already.
|
|
72
|
+
*/
|
|
73
|
+
dropdownProps?: Omit<TabsDropdownProps, "tabs" | "selectedTabId" | "onTabSelected" | "aria-label" | "aria-labelledby">;
|
|
34
74
|
};
|
|
35
75
|
/**
|
|
36
76
|
* Renders the Tabs component when there is enough space to display the tabs as
|
|
@@ -19,7 +19,7 @@ type TabDropdownItem = {
|
|
|
19
19
|
*/
|
|
20
20
|
testId?: string;
|
|
21
21
|
};
|
|
22
|
-
type
|
|
22
|
+
export type TabsDropdownProps = AriaLabelOrAriaLabelledby & {
|
|
23
23
|
/**
|
|
24
24
|
* A unique id for the component. If not provided, a unique base id will be
|
|
25
25
|
* generated automatically.
|
|
@@ -81,5 +81,5 @@ type Props = AriaLabelOrAriaLabelledby & {
|
|
|
81
81
|
* in the ResponsiveTabs component. Please reach out to the WB team if there is
|
|
82
82
|
* a need to use this component directly.
|
|
83
83
|
*/
|
|
84
|
-
export declare const TabsDropdown: React.ForwardRefExoticComponent<
|
|
84
|
+
export declare const TabsDropdown: React.ForwardRefExoticComponent<TabsDropdownProps & React.RefAttributes<HTMLDivElement>>;
|
|
85
85
|
export {};
|
|
@@ -37,7 +37,7 @@ export type TabItem = AriaProps & {
|
|
|
37
37
|
*/
|
|
38
38
|
testId?: string;
|
|
39
39
|
};
|
|
40
|
-
type
|
|
40
|
+
export type TabsProps = {
|
|
41
41
|
/**
|
|
42
42
|
* A unique id to use as the base of the ids for the elements within the
|
|
43
43
|
* component. If the `id` prop is not provided, a base unique id will be
|
|
@@ -131,5 +131,4 @@ type Props = {
|
|
|
131
131
|
* For responsive cases where the tabs should switch to a dropdown when there is
|
|
132
132
|
* not enough horizontal space, use the `ResponsiveTabs` component.
|
|
133
133
|
*/
|
|
134
|
-
export declare const Tabs: React.ForwardRefExoticComponent<
|
|
135
|
-
export {};
|
|
134
|
+
export declare const Tabs: React.ForwardRefExoticComponent<TabsProps & React.RefAttributes<HTMLDivElement>>;
|
package/dist/es/index.js
CHANGED
|
@@ -31,6 +31,6 @@ function getTabId(tabId){return `${tabId}-tab`}function getTabPanelId(tabId){ret
|
|
|
31
31
|
|
|
32
32
|
const defaultLabels={defaultOpenerLabel:"Tabs"};const TabsDropdown=React.forwardRef((props,ref)=>{const{tabs,selectedTabId,onTabSelected,labels:labelsProp,opened,id:idProp,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,styles:stylesProp}=props;const labels=React.useMemo(()=>{return {...defaultLabels,...labelsProp}},[labelsProp]);const generatedUniqueId=React.useId();const uniqueId=idProp??generatedUniqueId;const openerId=`${uniqueId}-opener`;const panelId=`${uniqueId}-panel`;const selectedTabItem=React.useMemo(()=>{return tabs.find(tab=>tab.id===selectedTabId)},[tabs,selectedTabId]);if(tabs.length===0){return jsx(React.Fragment,{})}const menuText=selectedTabItem?.label||labels.defaultOpenerLabel;return jsxs(View,{ref:ref,id:uniqueId,testId:testId,style:stylesProp?.root,role:"region","aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,children:[jsx(ActionMenu,{opened:opened,id:openerId,menuText:menuText,opener:()=>jsx(Button,{testId:testId?`${testId}-opener`:undefined,kind:"tertiary",endIcon:caretDown,style:[styles$1.opener,stylesProp?.opener],children:menuText}),style:[styles$1.actionMenu,stylesProp?.actionMenu],children:tabs.map(tab=>{return jsx(ActionItem,{label:tab.label,onClick:()=>{onTabSelected(tab.id);},active:tab.id===selectedTabId,testId:tab.testId,rightAccessory:tab.id===selectedTabId?jsx(PhosphorIcon,{icon:checkCircleIcon,size:"medium","aria-hidden":"true"}):undefined},tab.id)})}),jsx(View,{id:panelId,role:"group","aria-labelledby":openerId,testId:testId?`${testId}-panel`:undefined,children:selectedTabItem?.panel})]})});const styles$1=StyleSheet.create({actionMenu:{width:"100%",alignItems:"flex-start",borderBlockEnd:`${border.width.thin} solid ${semanticColor.core.border.neutral.subtle}`},opener:{position:"relative",height:"unset",paddingBlockStart:sizing.size_120,paddingBlockEnd:sizing.size_140,paddingInline:sizing.size_180,width:"100%",justifyContent:"space-between"}});
|
|
33
33
|
|
|
34
|
-
const ResponsiveTabs=props=>{const{tabs,selectedTabId,onTabSelected,onLayoutChange,...ariaProps}=props;const[showDropdown,setShowDropdown]=React.useState(false);const tabsRef=React.useRef(null);const containerRef=React.useRef(null);const tabsWidthRef=React.useRef(null);const tabsSignature=React.useMemo(()=>tabs.map(t=>`${t.id}:${t.label}`).join("|"),[tabs]);const checkOverflow=React.useCallback(()=>{const container=containerRef.current;if(!container){return}if(!showDropdown&&tabsRef.current){const tablistWrapper=tabsRef.current.firstElementChild;if(tablistWrapper){const hasOverflow=tablistWrapper.scrollWidth>tablistWrapper.clientWidth;if(hasOverflow){tabsWidthRef.current=tablistWrapper.scrollWidth;setShowDropdown(true);}}}else if(showDropdown&&tabsWidthRef.current){const containerWidth=container.clientWidth;if(containerWidth>=tabsWidthRef.current){setShowDropdown(false);}}},[showDropdown,containerRef]);React.useEffect(()=>{if(showDropdown){tabsWidthRef.current=null;setShowDropdown(false);}else {checkOverflow();}},[tabsSignature]);React.useEffect(()=>{const container=containerRef.current;if(!container||!window.ResizeObserver){return}const resizeObserver=new ResizeObserver(()=>{checkOverflow();});resizeObserver.observe(container);return ()=>{resizeObserver.disconnect();}},[checkOverflow]);React.useEffect(()=>{onLayoutChange?.(showDropdown?"dropdown":"tabs");},[showDropdown,onLayoutChange]);return jsx(
|
|
34
|
+
const ResponsiveTabs=props=>{const{tabs,selectedTabId,onTabSelected,onLayoutChange,id,testId,tabsProps,dropdownProps,...ariaProps}=props;const[showDropdown,setShowDropdown]=React.useState(false);const tabsRef=React.useRef(null);const containerRef=React.useRef(null);const tabsWidthRef=React.useRef(null);const tabsSignature=React.useMemo(()=>tabs.map(t=>`${t.id}:${t.label}`).join("|"),[tabs]);const checkOverflow=React.useCallback(()=>{const container=containerRef.current;if(!container){return}if(!showDropdown&&tabsRef.current){const tablistWrapper=tabsRef.current.firstElementChild;if(tablistWrapper){const hasOverflow=tablistWrapper.scrollWidth>tablistWrapper.clientWidth;if(hasOverflow){tabsWidthRef.current=tablistWrapper.scrollWidth;setShowDropdown(true);}}}else if(showDropdown&&tabsWidthRef.current){const containerWidth=container.clientWidth;if(containerWidth>=tabsWidthRef.current){setShowDropdown(false);}}},[showDropdown,containerRef]);React.useEffect(()=>{if(showDropdown){tabsWidthRef.current=null;setShowDropdown(false);}else {checkOverflow();}},[tabsSignature]);React.useEffect(()=>{const container=containerRef.current;if(!container||!window.ResizeObserver){return}const resizeObserver=new ResizeObserver(()=>{checkOverflow();});resizeObserver.observe(container);return ()=>{resizeObserver.disconnect();}},[checkOverflow]);React.useEffect(()=>{onLayoutChange?.(showDropdown?"dropdown":"tabs");},[showDropdown,onLayoutChange]);return jsx(View,{ref:containerRef,style:styles.container,id:id,testId:testId,children:showDropdown?createElement(TabsDropdown,{...dropdownProps,...ariaProps,key:"dropdown",tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,styles:{root:styles.fadeIn}}):createElement(Tabs,{...tabsProps,...ariaProps,key:"tabs",ref:tabsRef,tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,styles:{root:[styles.fadeIn,{maxWidth:"100%",width:"100%"}]}})})};const fadeInKeyframes={from:{opacity:0},to:{opacity:1}};const styles=StyleSheet.create({fadeIn:{animationName:fadeInKeyframes,animationDuration:"150ms",animationTimingFunction:"ease-in-out"},container:{width:"100%"}});
|
|
35
35
|
|
|
36
36
|
export { NavigationTabItem, NavigationTabs, ResponsiveTabs, Tab, Tabs };
|
package/dist/index.js
CHANGED
|
@@ -59,7 +59,7 @@ function getTabId(tabId){return `${tabId}-tab`}function getTabPanelId(tabId){ret
|
|
|
59
59
|
|
|
60
60
|
const defaultLabels={defaultOpenerLabel:"Tabs"};const TabsDropdown=React__namespace.forwardRef((props,ref)=>{const{tabs,selectedTabId,onTabSelected,labels:labelsProp,opened,id:idProp,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,styles:stylesProp}=props;const labels=React__namespace.useMemo(()=>{return {...defaultLabels,...labelsProp}},[labelsProp]);const generatedUniqueId=React__namespace.useId();const uniqueId=idProp??generatedUniqueId;const openerId=`${uniqueId}-opener`;const panelId=`${uniqueId}-panel`;const selectedTabItem=React__namespace.useMemo(()=>{return tabs.find(tab=>tab.id===selectedTabId)},[tabs,selectedTabId]);if(tabs.length===0){return jsxRuntime.jsx(React__namespace.Fragment,{})}const menuText=selectedTabItem?.label||labels.defaultOpenerLabel;return jsxRuntime.jsxs(wonderBlocksCore.View,{ref:ref,id:uniqueId,testId:testId,style:stylesProp?.root,role:"region","aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,children:[jsxRuntime.jsx(wonderBlocksDropdown.ActionMenu,{opened:opened,id:openerId,menuText:menuText,opener:()=>jsxRuntime.jsx(Button__default["default"],{testId:testId?`${testId}-opener`:undefined,kind:"tertiary",endIcon:caretDown__default["default"],style:[styles$1.opener,stylesProp?.opener],children:menuText}),style:[styles$1.actionMenu,stylesProp?.actionMenu],children:tabs.map(tab=>{return jsxRuntime.jsx(wonderBlocksDropdown.ActionItem,{label:tab.label,onClick:()=>{onTabSelected(tab.id);},active:tab.id===selectedTabId,testId:tab.testId,rightAccessory:tab.id===selectedTabId?jsxRuntime.jsx(wonderBlocksIcon.PhosphorIcon,{icon:checkCircleIcon__default["default"],size:"medium","aria-hidden":"true"}):undefined},tab.id)})}),jsxRuntime.jsx(wonderBlocksCore.View,{id:panelId,role:"group","aria-labelledby":openerId,testId:testId?`${testId}-panel`:undefined,children:selectedTabItem?.panel})]})});const styles$1=aphrodite.StyleSheet.create({actionMenu:{width:"100%",alignItems:"flex-start",borderBlockEnd:`${wonderBlocksTokens.border.width.thin} solid ${wonderBlocksTokens.semanticColor.core.border.neutral.subtle}`},opener:{position:"relative",height:"unset",paddingBlockStart:wonderBlocksTokens.sizing.size_120,paddingBlockEnd:wonderBlocksTokens.sizing.size_140,paddingInline:wonderBlocksTokens.sizing.size_180,width:"100%",justifyContent:"space-between"}});
|
|
61
61
|
|
|
62
|
-
const ResponsiveTabs=props=>{const{tabs,selectedTabId,onTabSelected,onLayoutChange,...ariaProps}=props;const[showDropdown,setShowDropdown]=React__namespace.useState(false);const tabsRef=React__namespace.useRef(null);const containerRef=React__namespace.useRef(null);const tabsWidthRef=React__namespace.useRef(null);const tabsSignature=React__namespace.useMemo(()=>tabs.map(t=>`${t.id}:${t.label}`).join("|"),[tabs]);const checkOverflow=React__namespace.useCallback(()=>{const container=containerRef.current;if(!container){return}if(!showDropdown&&tabsRef.current){const tablistWrapper=tabsRef.current.firstElementChild;if(tablistWrapper){const hasOverflow=tablistWrapper.scrollWidth>tablistWrapper.clientWidth;if(hasOverflow){tabsWidthRef.current=tablistWrapper.scrollWidth;setShowDropdown(true);}}}else if(showDropdown&&tabsWidthRef.current){const containerWidth=container.clientWidth;if(containerWidth>=tabsWidthRef.current){setShowDropdown(false);}}},[showDropdown,containerRef]);React__namespace.useEffect(()=>{if(showDropdown){tabsWidthRef.current=null;setShowDropdown(false);}else {checkOverflow();}},[tabsSignature]);React__namespace.useEffect(()=>{const container=containerRef.current;if(!container||!window.ResizeObserver){return}const resizeObserver=new ResizeObserver(()=>{checkOverflow();});resizeObserver.observe(container);return ()=>{resizeObserver.disconnect();}},[checkOverflow]);React__namespace.useEffect(()=>{onLayoutChange?.(showDropdown?"dropdown":"tabs");},[showDropdown,onLayoutChange]);return jsxRuntime.jsx(
|
|
62
|
+
const ResponsiveTabs=props=>{const{tabs,selectedTabId,onTabSelected,onLayoutChange,id,testId,tabsProps,dropdownProps,...ariaProps}=props;const[showDropdown,setShowDropdown]=React__namespace.useState(false);const tabsRef=React__namespace.useRef(null);const containerRef=React__namespace.useRef(null);const tabsWidthRef=React__namespace.useRef(null);const tabsSignature=React__namespace.useMemo(()=>tabs.map(t=>`${t.id}:${t.label}`).join("|"),[tabs]);const checkOverflow=React__namespace.useCallback(()=>{const container=containerRef.current;if(!container){return}if(!showDropdown&&tabsRef.current){const tablistWrapper=tabsRef.current.firstElementChild;if(tablistWrapper){const hasOverflow=tablistWrapper.scrollWidth>tablistWrapper.clientWidth;if(hasOverflow){tabsWidthRef.current=tablistWrapper.scrollWidth;setShowDropdown(true);}}}else if(showDropdown&&tabsWidthRef.current){const containerWidth=container.clientWidth;if(containerWidth>=tabsWidthRef.current){setShowDropdown(false);}}},[showDropdown,containerRef]);React__namespace.useEffect(()=>{if(showDropdown){tabsWidthRef.current=null;setShowDropdown(false);}else {checkOverflow();}},[tabsSignature]);React__namespace.useEffect(()=>{const container=containerRef.current;if(!container||!window.ResizeObserver){return}const resizeObserver=new ResizeObserver(()=>{checkOverflow();});resizeObserver.observe(container);return ()=>{resizeObserver.disconnect();}},[checkOverflow]);React__namespace.useEffect(()=>{onLayoutChange?.(showDropdown?"dropdown":"tabs");},[showDropdown,onLayoutChange]);return jsxRuntime.jsx(wonderBlocksCore.View,{ref:containerRef,style:styles.container,id:id,testId:testId,children:showDropdown?React.createElement(TabsDropdown,{...dropdownProps,...ariaProps,key:"dropdown",tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,styles:{root:styles.fadeIn}}):React.createElement(Tabs,{...tabsProps,...ariaProps,key:"tabs",ref:tabsRef,tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,styles:{root:[styles.fadeIn,{maxWidth:"100%",width:"100%"}]}})})};const fadeInKeyframes={from:{opacity:0},to:{opacity:1}};const styles=aphrodite.StyleSheet.create({fadeIn:{animationName:fadeInKeyframes,animationDuration:"150ms",animationTimingFunction:"ease-in-out"},container:{width:"100%"}});
|
|
63
63
|
|
|
64
64
|
exports.NavigationTabItem = NavigationTabItem;
|
|
65
65
|
exports.NavigationTabs = NavigationTabs;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Tabs are used to control what content is shown",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "0.0.0-PR2913-
|
|
6
|
+
"version": "0.0.0-PR2913-20251218223804",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@phosphor-icons/core": "^2.0.2",
|
|
24
24
|
"@khanacademy/wonder-blocks-button": "11.2.10",
|
|
25
|
+
"@khanacademy/wonder-blocks-dropdown": "10.6.1",
|
|
25
26
|
"@khanacademy/wonder-blocks-core": "12.4.2",
|
|
26
27
|
"@khanacademy/wonder-blocks-icon": "5.3.5",
|
|
27
|
-
"@khanacademy/wonder-blocks-
|
|
28
|
-
"@khanacademy/wonder-blocks-
|
|
29
|
-
"@khanacademy/wonder-blocks-tokens": "14.1.3"
|
|
28
|
+
"@khanacademy/wonder-blocks-tokens": "14.1.3",
|
|
29
|
+
"@khanacademy/wonder-blocks-typography": "4.2.27"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"aphrodite": "^1.2.5",
|