@primer/react 38.39.0-rc.790ecbbc6 → 38.39.0-rc.9b98b8041
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 +2 -0
- package/dist/FeatureFlags/DefaultFeatureFlags.js +1 -2
- package/dist/experimental/Tabs/Tabs.js +21 -24
- package/dist/experimental/Tabs/useTabList.js +16 -20
- package/dist/experimental/UnderlinePanels/UnderlinePanels.js +8 -13
- package/generated/components.json +6 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
- [#8371](https://github.com/primer/react/pull/8371) [`dc8387f`](https://github.com/primer/react/commit/dc8387f4fa07cf00887cb2412d358fb7eb54be63) Thanks [@rickyzhangca](https://github.com/rickyzhangca)! - DataTable: Keep sortable column names concise and convey the next sort action as an accessible description
|
|
16
16
|
|
|
17
|
+
- [#8352](https://github.com/primer/react/pull/8352) [`4dc92ec`](https://github.com/primer/react/commit/4dc92ecc5014f4c5f7675ec64d11cd0e0d5aa83e) Thanks [@rickyzhangca](https://github.com/rickyzhangca)! - Tabs and UnderlinePanels: Make the controlled selection and manual activation APIs available without a feature flag. Re-selecting the active tab no longer fires the value change callback.
|
|
18
|
+
|
|
17
19
|
## 38.38.0
|
|
18
20
|
|
|
19
21
|
### Minor Changes
|
|
@@ -8,8 +8,7 @@ const DefaultFeatureFlags = FeatureFlagScope.create({
|
|
|
8
8
|
primer_react_action_list_group_heading_trailing_action: false,
|
|
9
9
|
primer_react_action_list_item_gap: false,
|
|
10
10
|
primer_react_timeline_list_semantics: false,
|
|
11
|
-
primer_react_merged_forwarded_refs: false
|
|
12
|
-
primer_react_underline_panels_controlled: false
|
|
11
|
+
primer_react_merged_forwarded_refs: false
|
|
13
12
|
});
|
|
14
13
|
//#endregion
|
|
15
14
|
export { DefaultFeatureFlags };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { c } from "../../utils/react-compiler-runtime.js";
|
|
2
2
|
import useIsomorphicLayoutEffect from "../../utils/useIsomorphicLayoutEffect.js";
|
|
3
|
-
import { useFeatureFlag } from "../../FeatureFlags/useFeatureFlag.js";
|
|
4
3
|
import { useControllableState } from "../../hooks/useControllableState.js";
|
|
5
4
|
import { TabsContext } from "./TabsContext.js";
|
|
6
5
|
import React, { useId } from "react";
|
|
@@ -15,12 +14,11 @@ import { jsx } from "react/jsx-runtime";
|
|
|
15
14
|
*/
|
|
16
15
|
function Tabs(props) {
|
|
17
16
|
var _props$id, _props$activationMode, _props$defaultValue;
|
|
18
|
-
const $ = c(
|
|
17
|
+
const $ = c(15);
|
|
19
18
|
const { children, onValueChange } = props;
|
|
20
19
|
const generatedId = useId();
|
|
21
20
|
const groupId = (_props$id = props.id) !== null && _props$id !== void 0 ? _props$id : generatedId;
|
|
22
|
-
const
|
|
23
|
-
const activationMode = controlledApiEnabled ? (_props$activationMode = props.activationMode) !== null && _props$activationMode !== void 0 ? _props$activationMode : "automatic" : "automatic";
|
|
21
|
+
const activationMode = (_props$activationMode = props.activationMode) !== null && _props$activationMode !== void 0 ? _props$activationMode : "automatic";
|
|
24
22
|
const t0 = (_props$defaultValue = props.defaultValue) !== null && _props$defaultValue !== void 0 ? _props$defaultValue : props.value;
|
|
25
23
|
let t1;
|
|
26
24
|
if ($[0] !== props.value || $[1] !== t0) {
|
|
@@ -37,7 +35,7 @@ function Tabs(props) {
|
|
|
37
35
|
const [focusedValue, setFocusedValue] = React.useState();
|
|
38
36
|
const savedOnValueChange = React.useRef(onValueChange);
|
|
39
37
|
let t2;
|
|
40
|
-
if ($[3] !== activationMode || $[4] !==
|
|
38
|
+
if ($[3] !== activationMode || $[4] !== focusedValue || $[5] !== groupId || $[6] !== selectedValue || $[7] !== setSelectedValue) {
|
|
41
39
|
t2 = {
|
|
42
40
|
groupId,
|
|
43
41
|
selectedValue,
|
|
@@ -46,7 +44,7 @@ function Tabs(props) {
|
|
|
46
44
|
selectTab(value) {
|
|
47
45
|
var _savedOnValueChange$c;
|
|
48
46
|
setFocusedValue(void 0);
|
|
49
|
-
if (
|
|
47
|
+
if (value === selectedValue) return;
|
|
50
48
|
setSelectedValue(value);
|
|
51
49
|
(_savedOnValueChange$c = savedOnValueChange.current) === null || _savedOnValueChange$c === void 0 || _savedOnValueChange$c.call(savedOnValueChange, { value });
|
|
52
50
|
},
|
|
@@ -55,39 +53,38 @@ function Tabs(props) {
|
|
|
55
53
|
}
|
|
56
54
|
};
|
|
57
55
|
$[3] = activationMode;
|
|
58
|
-
$[4] =
|
|
59
|
-
$[5] =
|
|
60
|
-
$[6] =
|
|
61
|
-
$[7] =
|
|
62
|
-
$[8] =
|
|
63
|
-
|
|
64
|
-
} else t2 = $[9];
|
|
56
|
+
$[4] = focusedValue;
|
|
57
|
+
$[5] = groupId;
|
|
58
|
+
$[6] = selectedValue;
|
|
59
|
+
$[7] = setSelectedValue;
|
|
60
|
+
$[8] = t2;
|
|
61
|
+
} else t2 = $[8];
|
|
65
62
|
const contextValue = t2;
|
|
66
63
|
let t3;
|
|
67
64
|
let t4;
|
|
68
|
-
if ($[
|
|
65
|
+
if ($[9] !== onValueChange) {
|
|
69
66
|
t3 = () => {
|
|
70
67
|
savedOnValueChange.current = onValueChange;
|
|
71
68
|
};
|
|
72
69
|
t4 = [onValueChange];
|
|
73
|
-
$[
|
|
74
|
-
$[
|
|
75
|
-
$[
|
|
70
|
+
$[9] = onValueChange;
|
|
71
|
+
$[10] = t3;
|
|
72
|
+
$[11] = t4;
|
|
76
73
|
} else {
|
|
77
|
-
t3 = $[
|
|
78
|
-
t4 = $[
|
|
74
|
+
t3 = $[10];
|
|
75
|
+
t4 = $[11];
|
|
79
76
|
}
|
|
80
77
|
useIsomorphicLayoutEffect(t3, t4);
|
|
81
78
|
let t5;
|
|
82
|
-
if ($[
|
|
79
|
+
if ($[12] !== children || $[13] !== contextValue) {
|
|
83
80
|
t5 = /*#__PURE__*/ jsx(TabsContext.Provider, {
|
|
84
81
|
value: contextValue,
|
|
85
82
|
children
|
|
86
83
|
});
|
|
87
|
-
$[
|
|
88
|
-
$[
|
|
89
|
-
$[
|
|
90
|
-
} else t5 = $[
|
|
84
|
+
$[12] = children;
|
|
85
|
+
$[13] = contextValue;
|
|
86
|
+
$[14] = t5;
|
|
87
|
+
} else t5 = $[14];
|
|
91
88
|
return t5;
|
|
92
89
|
}
|
|
93
90
|
//#endregion
|
|
@@ -5,27 +5,24 @@ import { useFeatureFlag } from "../../FeatureFlags/useFeatureFlag.js";
|
|
|
5
5
|
import { useRef } from "react";
|
|
6
6
|
//#region src/experimental/Tabs/useTabList.ts
|
|
7
7
|
function useTabList(props) {
|
|
8
|
-
const $ = c(
|
|
8
|
+
const $ = c(9);
|
|
9
9
|
const { "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-orientation": ariaOrientation } = props;
|
|
10
10
|
const mergedRefEnabled = useFeatureFlag("primer_react_merged_forwarded_refs");
|
|
11
|
-
const controlledApiEnabled = useFeatureFlag("primer_react_underline_panels_controlled");
|
|
12
11
|
const tabListRef = useRef(null);
|
|
13
12
|
const mergedRef = useMergedRefs(tabListRef, props.ref);
|
|
14
13
|
const providedOrCreatedRef = useProvidedRefOrCreate(props.ref);
|
|
15
14
|
const readRef = mergedRefEnabled ? tabListRef : providedOrCreatedRef;
|
|
16
15
|
const appliedRef = mergedRefEnabled ? mergedRef : providedOrCreatedRef;
|
|
17
16
|
let t0;
|
|
18
|
-
if ($[0] !== ariaOrientation || $[1] !==
|
|
17
|
+
if ($[0] !== ariaOrientation || $[1] !== readRef) {
|
|
19
18
|
t0 = (event) => {
|
|
20
19
|
const { current: tablist } = readRef;
|
|
21
20
|
if (tablist === null) return;
|
|
22
21
|
const tabs = getFocusableTabs(tablist);
|
|
23
22
|
const getCurrentIndex = () => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (focusedIndex !== -1) return focusedIndex;
|
|
28
|
-
}
|
|
23
|
+
const activeElement = tablist.ownerDocument.activeElement;
|
|
24
|
+
const focusedIndex = activeElement instanceof HTMLElement ? tabs.indexOf(activeElement) : -1;
|
|
25
|
+
if (focusedIndex !== -1) return focusedIndex;
|
|
29
26
|
return tabs.findIndex(_temp);
|
|
30
27
|
};
|
|
31
28
|
const isVertical = ariaOrientation === "vertical";
|
|
@@ -50,14 +47,13 @@ function useTabList(props) {
|
|
|
50
47
|
}
|
|
51
48
|
};
|
|
52
49
|
$[0] = ariaOrientation;
|
|
53
|
-
$[1] =
|
|
54
|
-
$[2] =
|
|
55
|
-
|
|
56
|
-
} else t0 = $[3];
|
|
50
|
+
$[1] = readRef;
|
|
51
|
+
$[2] = t0;
|
|
52
|
+
} else t0 = $[2];
|
|
57
53
|
const onKeyDown = t0;
|
|
58
54
|
const t1 = ariaOrientation !== null && ariaOrientation !== void 0 ? ariaOrientation : "horizontal";
|
|
59
55
|
let t2;
|
|
60
|
-
if ($[
|
|
56
|
+
if ($[3] !== appliedRef || $[4] !== ariaLabel || $[5] !== ariaLabelledby || $[6] !== onKeyDown || $[7] !== t1) {
|
|
61
57
|
t2 = { tabListProps: {
|
|
62
58
|
ref: appliedRef,
|
|
63
59
|
"aria-label": ariaLabel,
|
|
@@ -66,13 +62,13 @@ function useTabList(props) {
|
|
|
66
62
|
role: "tablist",
|
|
67
63
|
onKeyDown
|
|
68
64
|
} };
|
|
69
|
-
$[
|
|
70
|
-
$[
|
|
71
|
-
$[
|
|
72
|
-
$[
|
|
73
|
-
$[
|
|
74
|
-
$[
|
|
75
|
-
} else t2 = $[
|
|
65
|
+
$[3] = appliedRef;
|
|
66
|
+
$[4] = ariaLabel;
|
|
67
|
+
$[5] = ariaLabelledby;
|
|
68
|
+
$[6] = onKeyDown;
|
|
69
|
+
$[7] = t1;
|
|
70
|
+
$[8] = t2;
|
|
71
|
+
} else t2 = $[8];
|
|
76
72
|
return t2;
|
|
77
73
|
}
|
|
78
74
|
function _temp(tab) {
|
|
@@ -3,7 +3,6 @@ import { useResizeObserver } from "../../hooks/useResizeObserver.js";
|
|
|
3
3
|
import { useId as useId$1 } from "../../hooks/useId.js";
|
|
4
4
|
import { warning } from "../../utils/warning.js";
|
|
5
5
|
import { isSlot } from "../../utils/is-slot.js";
|
|
6
|
-
import { useFeatureFlag } from "../../FeatureFlags/useFeatureFlag.js";
|
|
7
6
|
import { invariant } from "../../utils/invariant.js";
|
|
8
7
|
import { UnderlineItem, UnderlineItemList, UnderlineWrapper } from "../../internal/components/UnderlineTabbedInterface.js";
|
|
9
8
|
import UnderlinePanels_module_css_default from "./UnderlinePanels.module.css.js";
|
|
@@ -19,12 +18,8 @@ const UnderlinePanelsContext = /*#__PURE__*/ createContext({
|
|
|
19
18
|
iconsVisible: true,
|
|
20
19
|
loadingCounters: void 0
|
|
21
20
|
});
|
|
22
|
-
const UnderlinePanels = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, children, loadingCounters, className, value:
|
|
21
|
+
const UnderlinePanels = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, children, loadingCounters, className, value: controlledValue, defaultValue, onChange, activationMode = "automatic", ...props }) => {
|
|
23
22
|
var _tabValues$;
|
|
24
|
-
const controlledApiEnabled = useFeatureFlag("primer_react_underline_panels_controlled");
|
|
25
|
-
const controlledValue = controlledApiEnabled ? valueProp : void 0;
|
|
26
|
-
const defaultValue = controlledApiEnabled ? defaultValueProp : void 0;
|
|
27
|
-
const onChange = controlledApiEnabled ? onChangeProp : void 0;
|
|
28
23
|
const [iconsVisible, setIconsVisible] = useState(true);
|
|
29
24
|
const wrapperRef = useRef(null);
|
|
30
25
|
const listRef = useRef(null);
|
|
@@ -34,14 +29,14 @@ const UnderlinePanels = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabel
|
|
|
34
29
|
let panelIndex = 0;
|
|
35
30
|
const childrenWithProps = Children.map(children, (child) => {
|
|
36
31
|
if (/*#__PURE__*/ isValidElement(child) && (child.type === Tab || isSlot(child, Tab))) {
|
|
37
|
-
var
|
|
38
|
-
const value = (
|
|
32
|
+
var _child$props$value;
|
|
33
|
+
const value = (_child$props$value = child.props.value) !== null && _child$props$value !== void 0 ? _child$props$value : `${tabIndex}`;
|
|
39
34
|
tabIndex++;
|
|
40
35
|
return /*#__PURE__*/ cloneElement(child, { value });
|
|
41
36
|
}
|
|
42
37
|
if (/*#__PURE__*/ isValidElement(child) && (child.type === Panel || isSlot(child, Panel))) {
|
|
43
|
-
var
|
|
44
|
-
const value = (
|
|
38
|
+
var _child$props$value2;
|
|
39
|
+
const value = (_child$props$value2 = child.props.value) !== null && _child$props$value2 !== void 0 ? _child$props$value2 : `${panelIndex}`;
|
|
45
40
|
panelIndex++;
|
|
46
41
|
return /*#__PURE__*/ cloneElement(child, { value });
|
|
47
42
|
}
|
|
@@ -74,11 +69,11 @@ const UnderlinePanels = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabel
|
|
|
74
69
|
tabValues,
|
|
75
70
|
panelValues
|
|
76
71
|
];
|
|
77
|
-
}, [children
|
|
72
|
+
}, [children]);
|
|
78
73
|
const isControlled = controlledValue !== void 0;
|
|
79
74
|
const [uncontrolledValue, setUncontrolledValue] = useState(() => {
|
|
80
|
-
var
|
|
81
|
-
return (
|
|
75
|
+
var _ref;
|
|
76
|
+
return (_ref = defaultValue !== null && defaultValue !== void 0 ? defaultValue : selectedFromProps) !== null && _ref !== void 0 ? _ref : "0";
|
|
82
77
|
});
|
|
83
78
|
const [prevSelectedFromProps, setPrevSelectedFromProps] = useState(selectedFromProps);
|
|
84
79
|
if (!isControlled && selectedFromProps !== prevSelectedFromProps) {
|
|
@@ -9775,19 +9775,19 @@
|
|
|
9775
9775
|
},
|
|
9776
9776
|
{
|
|
9777
9777
|
"id": "experimental-components-underlinepanels-features--controlled",
|
|
9778
|
-
"code": "() => {\n const [refType, setRefType] = useState('branch')\n return (\n <>\n <
|
|
9778
|
+
"code": "() => {\n const [refType, setRefType] = useState('branch')\n return (\n <>\n <UnderlinePanels\n aria-label=\"Ref type\"\n value={refType}\n onChange={({ value }) => {\n action('onChange')({\n value,\n })\n setRefType(value)\n }}\n >\n <UnderlinePanels.Tab value=\"branch\" icon={GitBranchIcon}>\n Branches\n </UnderlinePanels.Tab>\n <UnderlinePanels.Tab value=\"tag\" icon={TagIcon}>\n Tags\n </UnderlinePanels.Tab>\n <UnderlinePanels.Panel value=\"branch\">\n Find or create a branch…\n </UnderlinePanels.Panel>\n <UnderlinePanels.Panel value=\"tag\">\n Search or create a new tag…\n </UnderlinePanels.Panel>\n </UnderlinePanels>\n <p>\n Selected ref type: <strong>{refType}</strong>\n </p>\n </>\n )\n}"
|
|
9779
9779
|
},
|
|
9780
9780
|
{
|
|
9781
9781
|
"id": "experimental-components-underlinepanels-features--uncontrolled",
|
|
9782
|
-
"code": "() => (\n <>\n <
|
|
9782
|
+
"code": "() => (\n <>\n <UnderlinePanels\n aria-label=\"Ref type\"\n defaultValue=\"tag\"\n onChange={({ value }) => {\n action('onChange')({\n value,\n })\n }}\n >\n <UnderlinePanels.Tab value=\"branch\">Branches</UnderlinePanels.Tab>\n <UnderlinePanels.Tab value=\"tag\">Tags</UnderlinePanels.Tab>\n <UnderlinePanels.Panel value=\"branch\">\n Find or create a branch…\n </UnderlinePanels.Panel>\n <UnderlinePanels.Panel value=\"tag\">\n Search or create a new tag…\n </UnderlinePanels.Panel>\n </UnderlinePanels>\n </>\n)"
|
|
9783
9783
|
},
|
|
9784
9784
|
{
|
|
9785
9785
|
"id": "experimental-components-underlinepanels-features--manual-activation",
|
|
9786
|
-
"code": "() => {\n const [refType, setRefType] = useState('branch')\n return (\n <>\n <
|
|
9786
|
+
"code": "() => {\n const [refType, setRefType] = useState('branch')\n return (\n <>\n <p>\n With <code>activationMode="manual"</code>, arrow keys only\n move focus; press Enter or Space (or click) to commit selection. Prefer\n this when switching tabs triggers async work like a fetch.\n </p>\n <UnderlinePanels\n aria-label=\"Ref type\"\n value={refType}\n activationMode=\"manual\"\n onChange={({ value }) => {\n action('onChange')({\n value,\n })\n setRefType(value)\n }}\n >\n <UnderlinePanels.Tab value=\"branch\">Branches</UnderlinePanels.Tab>\n <UnderlinePanels.Tab value=\"tag\">Tags</UnderlinePanels.Tab>\n <UnderlinePanels.Panel value=\"branch\">\n Find or create a branch…\n </UnderlinePanels.Panel>\n <UnderlinePanels.Panel value=\"tag\">\n Search or create a new tag…\n </UnderlinePanels.Panel>\n </UnderlinePanels>\n </>\n )\n}"
|
|
9787
9787
|
},
|
|
9788
9788
|
{
|
|
9789
9789
|
"id": "experimental-components-underlinepanels-features--in-overlay",
|
|
9790
|
-
"code": "() => {\n const [open, setOpen] = useState(false)\n const [refType, setRefType] = useState('branch')\n return (\n <>\n <
|
|
9790
|
+
"code": "() => {\n const [open, setOpen] = useState(false)\n const [refType, setRefType] = useState('branch')\n return (\n <>\n <AnchoredOverlay\n open={open}\n onOpen={() => setOpen(true)}\n onClose={() => setOpen(false)}\n renderAnchor={(props) => <Button {...props}>Select ref type</Button>}\n overlayProps={{\n role: 'dialog',\n 'aria-modal': true,\n 'aria-label': 'Select a ref type',\n style: {\n width: '320px',\n },\n }}\n focusZoneSettings={{\n disabled: true,\n }}\n >\n <UnderlinePanels\n aria-label=\"Ref type\"\n value={refType}\n onChange={({ value }) => {\n action('onChange')({\n value,\n })\n setRefType(value)\n }}\n >\n <UnderlinePanels.Tab value=\"branch\">Branches</UnderlinePanels.Tab>\n <UnderlinePanels.Tab value=\"tag\">Tags</UnderlinePanels.Tab>\n <UnderlinePanels.Panel value=\"branch\">\n Find or create a branch…\n </UnderlinePanels.Panel>\n <UnderlinePanels.Panel value=\"tag\">\n Search or create a new tag…\n </UnderlinePanels.Panel>\n </UnderlinePanels>\n </AnchoredOverlay>\n </>\n )\n}"
|
|
9791
9791
|
}
|
|
9792
9792
|
],
|
|
9793
9793
|
"importPath": "@primer/react/experimental",
|
|
@@ -9808,7 +9808,7 @@
|
|
|
9808
9808
|
"name": "value",
|
|
9809
9809
|
"type": "string",
|
|
9810
9810
|
"defaultValue": "",
|
|
9811
|
-
"description": "The value of the selected tab, keyed to each `UnderlinePanels.Tab`/`UnderlinePanels.Panel` `value`. Provide this (with `onChange`) for a controlled component where the selected tab is the single source of truth.
|
|
9811
|
+
"description": "The value of the selected tab, keyed to each `UnderlinePanels.Tab`/`UnderlinePanels.Panel` `value`. Provide this (with `onChange`) for a controlled component where the selected tab is the single source of truth."
|
|
9812
9812
|
},
|
|
9813
9813
|
{
|
|
9814
9814
|
"name": "defaultValue",
|
|
@@ -9826,7 +9826,7 @@
|
|
|
9826
9826
|
"name": "activationMode",
|
|
9827
9827
|
"type": "'automatic' | 'manual'",
|
|
9828
9828
|
"defaultValue": "'automatic'",
|
|
9829
|
-
"description": "Controls how tabs are activated with the keyboard. `'automatic'` selects on focus (Arrow/Home/End select immediately); `'manual'` moves focus only and commits selection on Enter, Space, or click. Prefer `'manual'` when displaying a panel is not instant (e.g. it triggers a network request).
|
|
9829
|
+
"description": "Controls how tabs are activated with the keyboard. `'automatic'` selects on focus (Arrow/Home/End select immediately); `'manual'` moves focus only and commits selection on Enter, Space, or click. Prefer `'manual'` when displaying a panel is not instant (e.g. it triggers a network request)."
|
|
9830
9830
|
},
|
|
9831
9831
|
{
|
|
9832
9832
|
"name": "children",
|
package/package.json
CHANGED