@seed-design/lynx-react 0.5.2 → 0.6.0
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/lib/components/Checkbox/Checkbox.d.ts +26 -0
- package/lib/components/Checkbox/Checkbox.d.ts.map +1 -1
- package/lib/components/Checkbox/Checkbox.jsx +1 -1
- package/lib/components/Icon/Icon.d.ts +5 -0
- package/lib/components/Icon/Icon.d.ts.map +1 -1
- package/lib/components/Icon/Icon.jsx +14 -3
- package/lib/components/MannerTemp/MannerTemp.d.ts +19 -0
- package/lib/components/MannerTemp/MannerTemp.d.ts.map +1 -0
- package/lib/components/MannerTemp/MannerTemp.jsx +47 -0
- package/lib/components/MannerTemp/index.d.ts +2 -0
- package/lib/components/MannerTemp/index.d.ts.map +1 -0
- package/lib/components/MannerTempBadge/MannerTempBadge.d.ts +13 -0
- package/lib/components/MannerTempBadge/MannerTempBadge.d.ts.map +1 -0
- package/lib/components/MannerTempBadge/MannerTempBadge.jsx +15 -0
- package/lib/components/MannerTempBadge/index.d.ts +2 -0
- package/lib/components/MannerTempBadge/index.d.ts.map +1 -0
- package/lib/components/NotificationBadge/NotificationBadge.d.ts +18 -0
- package/lib/components/NotificationBadge/NotificationBadge.d.ts.map +1 -0
- package/lib/components/NotificationBadge/NotificationBadge.jsx +34 -0
- package/lib/components/NotificationBadge/index.d.ts +2 -0
- package/lib/components/NotificationBadge/index.d.ts.map +1 -0
- package/lib/components/PageBanner/PageBanner.d.ts +35 -0
- package/lib/components/PageBanner/PageBanner.d.ts.map +1 -0
- package/lib/components/PageBanner/PageBanner.jsx +150 -0
- package/lib/components/PageBanner/PageBanner.namespace.d.ts +2 -0
- package/lib/components/PageBanner/PageBanner.namespace.d.ts.map +1 -0
- package/lib/components/PageBanner/PageBanner.namespace.js +14 -0
- package/lib/components/PageBanner/index.d.ts +3 -0
- package/lib/components/PageBanner/index.d.ts.map +1 -0
- package/lib/components/RadioGroup/RadioGroup.d.ts +9 -0
- package/lib/components/RadioGroup/RadioGroup.d.ts.map +1 -1
- package/lib/components/RadioGroup/RadioGroup.jsx +1 -1
- package/lib/components/SegmentedControl/SegmentedControl.d.ts +27 -0
- package/lib/components/SegmentedControl/SegmentedControl.d.ts.map +1 -0
- package/lib/components/SegmentedControl/SegmentedControl.jsx +120 -0
- package/lib/components/SegmentedControl/SegmentedControl.namespace.d.ts +2 -0
- package/lib/components/SegmentedControl/SegmentedControl.namespace.d.ts.map +1 -0
- package/lib/components/SegmentedControl/SegmentedControl.namespace.js +10 -0
- package/lib/components/SegmentedControl/index.d.ts +3 -0
- package/lib/components/SegmentedControl/index.d.ts.map +1 -0
- package/lib/components/SelectBox/CheckSelectBox.namespace.d.ts +2 -0
- package/lib/components/SelectBox/CheckSelectBox.namespace.d.ts.map +1 -0
- package/lib/components/SelectBox/CheckSelectBox.namespace.js +17 -0
- package/lib/components/SelectBox/RadioSelectBox.namespace.d.ts +2 -0
- package/lib/components/SelectBox/RadioSelectBox.namespace.d.ts.map +1 -0
- package/lib/components/SelectBox/RadioSelectBox.namespace.js +15 -0
- package/lib/components/SelectBox/SelectBox.d.ts +85 -0
- package/lib/components/SelectBox/SelectBox.d.ts.map +1 -0
- package/lib/components/SelectBox/SelectBox.jsx +237 -0
- package/lib/components/SelectBox/index.d.ts +4 -0
- package/lib/components/SelectBox/index.d.ts.map +1 -0
- package/lib/components/index.d.ts +6 -0
- package/lib/components/index.d.ts.map +1 -1
- package/lib/hooks/useIconColor.d.ts +1 -0
- package/lib/hooks/useIconColor.d.ts.map +1 -1
- package/lib/hooks/useIconColor.js +4 -1
- package/lib/index.js +11 -1
- package/package.json +3 -3
- package/src/components/Checkbox/Checkbox.tsx +1 -1
- package/src/components/Icon/Icon.test.tsx +25 -1
- package/src/components/Icon/Icon.tsx +22 -1
- package/src/components/MannerTemp/MannerTemp.test.tsx +41 -0
- package/src/components/MannerTemp/MannerTemp.tsx +85 -0
- package/src/components/MannerTemp/index.ts +1 -0
- package/src/components/MannerTempBadge/MannerTempBadge.test.tsx +35 -0
- package/src/components/MannerTempBadge/MannerTempBadge.tsx +34 -0
- package/src/components/MannerTempBadge/index.ts +1 -0
- package/src/components/NotificationBadge/NotificationBadge.test.tsx +28 -0
- package/src/components/NotificationBadge/NotificationBadge.tsx +81 -0
- package/src/components/NotificationBadge/index.ts +1 -0
- package/src/components/PageBanner/PageBanner.namespace.ts +16 -0
- package/src/components/PageBanner/PageBanner.test.tsx +124 -0
- package/src/components/PageBanner/PageBanner.tsx +340 -0
- package/src/components/PageBanner/index.ts +18 -0
- package/src/components/RadioGroup/RadioGroup.tsx +1 -1
- package/src/components/SegmentedControl/SegmentedControl.namespace.ts +8 -0
- package/src/components/SegmentedControl/SegmentedControl.test.tsx +81 -0
- package/src/components/SegmentedControl/SegmentedControl.tsx +236 -0
- package/src/components/SegmentedControl/index.ts +10 -0
- package/src/components/SelectBox/CheckSelectBox.namespace.ts +22 -0
- package/src/components/SelectBox/RadioSelectBox.namespace.ts +18 -0
- package/src/components/SelectBox/SelectBox.test.tsx +169 -0
- package/src/components/SelectBox/SelectBox.tsx +533 -0
- package/src/components/SelectBox/index.ts +3 -0
- package/src/components/index.ts +6 -0
- package/src/hooks/useIconColor.ts +6 -1
package/lib/index.js
CHANGED
|
@@ -27,8 +27,18 @@ import { Divider } from "./components/Divider/Divider.jsx";
|
|
|
27
27
|
import { FieldCharacterCount, FieldDescription, FieldErrorMessage, FieldFooter, FieldHeader, FieldIndicatorText, FieldLabel, FieldRequiredIndicator, FieldRoot } from "./components/Field/Field.jsx";
|
|
28
28
|
import { Field_namespace_exports } from "./components/Field/Field.namespace.js";
|
|
29
29
|
import { KeyboardAvoidingScrollView } from "./components/KeyboardAvoidingScrollView/KeyboardAvoidingScrollView.jsx";
|
|
30
|
+
import { MannerTemp, MannerTempEmote } from "./components/MannerTemp/MannerTemp.jsx";
|
|
31
|
+
import { MannerTempBadge } from "./components/MannerTempBadge/MannerTempBadge.jsx";
|
|
32
|
+
import { NotificationBadge, NotificationBadgePositioner } from "./components/NotificationBadge/NotificationBadge.jsx";
|
|
33
|
+
import { PageBannerBody, PageBannerButton, PageBannerCloseButton, PageBannerContent, PageBannerDescription, PageBannerRoot, PageBannerTitle } from "./components/PageBanner/PageBanner.jsx";
|
|
34
|
+
import { PageBanner_namespace_exports } from "./components/PageBanner/PageBanner.namespace.js";
|
|
30
35
|
import { RadioGroupItem, RadioGroupItemControl, RadioGroupItemIndicator, RadioGroupItemLabel, RadioGroupRoot } from "./components/RadioGroup/RadioGroup.jsx";
|
|
31
36
|
import { RadioGroup_namespace_exports } from "./components/RadioGroup/RadioGroup.namespace.js";
|
|
37
|
+
import { SegmentedControlIndicator, SegmentedControlItem, SegmentedControlRoot } from "./components/SegmentedControl/SegmentedControl.jsx";
|
|
38
|
+
import { SegmentedControl_namespace_exports } from "./components/SegmentedControl/SegmentedControl.namespace.js";
|
|
39
|
+
import { CheckSelectBoxBody, CheckSelectBoxCheckmarkControl, CheckSelectBoxCheckmarkIcon, CheckSelectBoxContent, CheckSelectBoxDescription, CheckSelectBoxFooter, CheckSelectBoxGroup, CheckSelectBoxLabel, CheckSelectBoxRoot, CheckSelectBoxTrigger, RadioSelectBoxBody, RadioSelectBoxContent, RadioSelectBoxDescription, RadioSelectBoxFooter, RadioSelectBoxGroup, RadioSelectBoxItem, RadioSelectBoxLabel, RadioSelectBoxTrigger } from "./components/SelectBox/SelectBox.jsx";
|
|
40
|
+
import { CheckSelectBox_namespace_exports } from "./components/SelectBox/CheckSelectBox.namespace.js";
|
|
41
|
+
import { RadioSelectBox_namespace_exports } from "./components/SelectBox/RadioSelectBox.namespace.js";
|
|
32
42
|
import { HStack, VStack } from "./components/Stack/Stack.jsx";
|
|
33
43
|
import { SwitchControl, SwitchLabel, SwitchRoot, SwitchThumb } from "./components/Switch/Switch.jsx";
|
|
34
44
|
import { Switch_namespace_exports } from "./components/Switch/Switch.namespace.js";
|
|
@@ -42,4 +52,4 @@ import { useTextFieldWithGraphemes } from "./components/TextField/useTextFieldWi
|
|
|
42
52
|
import { TextField_namespace_exports } from "./components/TextField/TextField.namespace.js";
|
|
43
53
|
import { getSeedClassName } from "./utils/get-seed-class-name.js";
|
|
44
54
|
import { useSeedClassName } from "./hooks/useSeedClassName.js";
|
|
45
|
-
export { Accordion_namespace_exports as Accordion, AccordionBody, AccordionContent, AccordionDescription, AccordionHeader, AccordionItem, AccordionPrefix, AccordionRoot, AccordionSuffixIcon, AccordionTitle, AccordionTrigger, ActionButton, AppBar_namespace_exports as AppBar, AppBarIconButton, AppBarLeft, AppBarMain, AppBarRight, AppBarRoot, AppBarSlot, AppBarSubtitle, AppBarTitle, Badge, BottomSheet_namespace_exports as BottomSheet, BottomSheetBackdrop, BottomSheetBody, BottomSheetContent, BottomSheetDescription, BottomSheetFooter, BottomSheetHandle, BottomSheetHeader, BottomSheetPositioner, BottomSheetRoot, BottomSheetTitle, BottomSheetTrigger, Box, Callout_namespace_exports as Callout, CalloutCloseButton, CalloutContent, CalloutDescription, CalloutLink, CalloutRoot, CalloutTitle, Checkbox_namespace_exports as Checkbox, CheckboxControl, CheckboxGroup, CheckboxIndicator, CheckboxLabel, CheckboxRoot, Chip_namespace_exports as Chip, ChipButton, ChipLabel, ChipPrefixAvatar, ChipPrefixIcon, ChipRadioItem, ChipRadioRoot, ChipRoot, ChipSuffixIcon, ChipToggle, Divider, Field_namespace_exports as Field, FieldCharacterCount, FieldDescription, FieldErrorMessage, FieldFooter, FieldHeader, FieldIndicatorText, FieldLabel, FieldRequiredIndicator, FieldRoot, HStack, Icon, KeyboardAvoidingScrollView, PrefixIcon, ProgressCircle_namespace_exports as ProgressCircle, ProgressCircleRange, ProgressCircleRoot, RadioGroup_namespace_exports as RadioGroup, RadioGroupItem, RadioGroupItemControl, RadioGroupItemIndicator, RadioGroupItemLabel, RadioGroupRoot, SuffixIcon, Switch_namespace_exports as Switch, SwitchControl, SwitchLabel, SwitchRoot, SwitchThumb, Tabs_namespace_exports as Tabs, TabsCarousel, TabsCarouselCamera, TabsContent, TabsIndicator, TabsList, TabsRoot, TabsTrigger, TagGroup_namespace_exports as TagGroup, TagGroupItem, TagGroupItemLabel, TagGroupRoot, Text, TextField_namespace_exports as TextField, TextFieldInput, TextFieldPrefixIcon, TextFieldPrefixText, TextFieldRoot, TextFieldSuffixIcon, TextFieldSuffixText, TextFieldTextarea, VStack, capitalize, createSlotRecipeContext, getSeedClassName, resolveRecipeToken, splitMultipleVariantsProps, useControllableState, useIconColor, usePressTap, useSafeArea, useSeedClassName, useTextFieldWithGraphemes };
|
|
55
|
+
export { Accordion_namespace_exports as Accordion, AccordionBody, AccordionContent, AccordionDescription, AccordionHeader, AccordionItem, AccordionPrefix, AccordionRoot, AccordionSuffixIcon, AccordionTitle, AccordionTrigger, ActionButton, AppBar_namespace_exports as AppBar, AppBarIconButton, AppBarLeft, AppBarMain, AppBarRight, AppBarRoot, AppBarSlot, AppBarSubtitle, AppBarTitle, Badge, BottomSheet_namespace_exports as BottomSheet, BottomSheetBackdrop, BottomSheetBody, BottomSheetContent, BottomSheetDescription, BottomSheetFooter, BottomSheetHandle, BottomSheetHeader, BottomSheetPositioner, BottomSheetRoot, BottomSheetTitle, BottomSheetTrigger, Box, Callout_namespace_exports as Callout, CalloutCloseButton, CalloutContent, CalloutDescription, CalloutLink, CalloutRoot, CalloutTitle, CheckSelectBox_namespace_exports as CheckSelectBox, CheckSelectBoxBody, CheckSelectBoxCheckmarkControl, CheckSelectBoxCheckmarkIcon, CheckSelectBoxContent, CheckSelectBoxDescription, CheckSelectBoxFooter, CheckSelectBoxGroup, CheckSelectBoxLabel, CheckSelectBoxRoot, CheckSelectBoxTrigger, Checkbox_namespace_exports as Checkbox, CheckboxControl, CheckboxGroup, CheckboxIndicator, CheckboxLabel, CheckboxRoot, Chip_namespace_exports as Chip, ChipButton, ChipLabel, ChipPrefixAvatar, ChipPrefixIcon, ChipRadioItem, ChipRadioRoot, ChipRoot, ChipSuffixIcon, ChipToggle, Divider, Field_namespace_exports as Field, FieldCharacterCount, FieldDescription, FieldErrorMessage, FieldFooter, FieldHeader, FieldIndicatorText, FieldLabel, FieldRequiredIndicator, FieldRoot, HStack, Icon, KeyboardAvoidingScrollView, MannerTemp, MannerTempBadge, MannerTempEmote, NotificationBadge, NotificationBadgePositioner, PageBanner_namespace_exports as PageBanner, PageBannerBody, PageBannerButton, PageBannerCloseButton, PageBannerContent, PageBannerDescription, PageBannerRoot, PageBannerTitle, PrefixIcon, ProgressCircle_namespace_exports as ProgressCircle, ProgressCircleRange, ProgressCircleRoot, RadioGroup_namespace_exports as RadioGroup, RadioGroupItem, RadioGroupItemControl, RadioGroupItemIndicator, RadioGroupItemLabel, RadioGroupRoot, RadioSelectBox_namespace_exports as RadioSelectBox, RadioSelectBoxBody, RadioSelectBoxContent, RadioSelectBoxDescription, RadioSelectBoxFooter, RadioSelectBoxGroup, RadioSelectBoxItem, RadioSelectBoxLabel, RadioSelectBoxTrigger, SegmentedControl_namespace_exports as SegmentedControl, SegmentedControlIndicator, SegmentedControlItem, SegmentedControlRoot, SuffixIcon, Switch_namespace_exports as Switch, SwitchControl, SwitchLabel, SwitchRoot, SwitchThumb, Tabs_namespace_exports as Tabs, TabsCarousel, TabsCarouselCamera, TabsContent, TabsIndicator, TabsList, TabsRoot, TabsTrigger, TagGroup_namespace_exports as TagGroup, TagGroupItem, TagGroupItemLabel, TagGroupRoot, Text, TextField_namespace_exports as TextField, TextFieldInput, TextFieldPrefixIcon, TextFieldPrefixText, TextFieldRoot, TextFieldSuffixIcon, TextFieldSuffixText, TextFieldTextarea, VStack, capitalize, createSlotRecipeContext, getSeedClassName, resolveRecipeToken, splitMultipleVariantsProps, useControllableState, useIconColor, usePressTap, useSafeArea, useSeedClassName, useTextFieldWithGraphemes };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seed-design/lynx-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/daangn/seed-design.git",
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"@lynx-js/react": ">=0.117.0",
|
|
36
36
|
"@lynx-js/types": ">=3.7.0",
|
|
37
|
-
"@seed-design/lynx-css": "0.0.0 || >=0.
|
|
37
|
+
"@seed-design/lynx-css": "0.0.0 || >=0.10.0 <1.0.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@lynx-js/react": "^0.117.0",
|
|
41
41
|
"@lynx-js/types": "^3.7.0",
|
|
42
|
-
"@seed-design/lynx-css": "0.
|
|
42
|
+
"@seed-design/lynx-css": "0.10.0",
|
|
43
43
|
"@testing-library/jest-dom": "^6.9.1",
|
|
44
44
|
"jsdom": "^29.0.2",
|
|
45
45
|
"preact": "npm:@lynx-js/internal-preact@10.28.4-ee7bb26",
|
|
@@ -47,7 +47,7 @@ interface CheckboxContextValue {
|
|
|
47
47
|
|
|
48
48
|
const CheckboxContext = React.createContext<CheckboxContextValue | null>(null);
|
|
49
49
|
|
|
50
|
-
function useCheckboxContext(consumer: string): CheckboxContextValue {
|
|
50
|
+
export function useCheckboxContext(consumer: string): CheckboxContextValue {
|
|
51
51
|
const ctx = React.useContext(CheckboxContext);
|
|
52
52
|
if (!ctx) {
|
|
53
53
|
throw new Error(`<${consumer}/> must be rendered inside <CheckboxRoot/>.`);
|
|
@@ -4,7 +4,7 @@ import type { MainThread } from "@lynx-js/types";
|
|
|
4
4
|
import { describe, expect, it } from "vitest";
|
|
5
5
|
|
|
6
6
|
import type { LynxIconElementProps } from "../../types";
|
|
7
|
-
import { InternalIcon } from "./Icon";
|
|
7
|
+
import { InternalIcon, PrefixIcon } from "./Icon";
|
|
8
8
|
|
|
9
9
|
const TestIcon = React.forwardRef<
|
|
10
10
|
MainThread.Element,
|
|
@@ -20,6 +20,15 @@ const TestIcon = React.forwardRef<
|
|
|
20
20
|
});
|
|
21
21
|
TestIcon.displayName = "TestIcon";
|
|
22
22
|
|
|
23
|
+
const MulticolorTestIcon = React.forwardRef<
|
|
24
|
+
MainThread.Element,
|
|
25
|
+
LynxIconElementProps & { children?: React.ReactNode }
|
|
26
|
+
>((props, ref) => <TestIcon {...props} ref={ref} />);
|
|
27
|
+
MulticolorTestIcon.displayName = "MulticolorTestIcon";
|
|
28
|
+
Object.assign(MulticolorTestIcon, {
|
|
29
|
+
[Symbol.for("@seed-design/multicolor-icon")]: true,
|
|
30
|
+
});
|
|
31
|
+
|
|
23
32
|
function getSourceAndImage() {
|
|
24
33
|
const source = elementTree.root?.querySelector<HTMLElement>("view");
|
|
25
34
|
const image = source?.querySelector<HTMLElement>("image");
|
|
@@ -127,3 +136,18 @@ describe("InternalIcon", () => {
|
|
|
127
136
|
expect(getSourceAndImage().image.getAttribute("tint-color")).toBe("rgb(73, 80, 88)");
|
|
128
137
|
});
|
|
129
138
|
});
|
|
139
|
+
|
|
140
|
+
describe("PrefixIcon", () => {
|
|
141
|
+
it("does not tint marked multicolor icons", async () => {
|
|
142
|
+
const { frames } = installMainThreadStyleMocks(() => "rgb(31, 35, 40)");
|
|
143
|
+
|
|
144
|
+
render(<PrefixIcon icon={<MulticolorTestIcon />} />, {
|
|
145
|
+
enableMainThread: true,
|
|
146
|
+
enableBackgroundThread: true,
|
|
147
|
+
});
|
|
148
|
+
await waitSchedule();
|
|
149
|
+
|
|
150
|
+
expect(frames.size).toBe(0);
|
|
151
|
+
expect(getSourceAndImage().image.getAttribute("tint-color")).toBeNull();
|
|
152
|
+
});
|
|
153
|
+
});
|
|
@@ -93,23 +93,42 @@ export function IconSlotProvider({
|
|
|
93
93
|
////////////////////////////////////////////////////////////////////////////////////
|
|
94
94
|
|
|
95
95
|
const iconSlotMarker = Symbol.for("@seed-design/lynx-react/icon-slot");
|
|
96
|
+
const seedMulticolorIconMarker = Symbol.for("@seed-design/multicolor-icon");
|
|
96
97
|
|
|
97
98
|
interface IconSlotComponent {
|
|
98
99
|
[iconSlotMarker]?: IconSlotName;
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
interface MulticolorIconComponent {
|
|
103
|
+
[seedMulticolorIconMarker]?: boolean;
|
|
104
|
+
}
|
|
105
|
+
|
|
101
106
|
export function getIconSlotName(node: React.ReactNode): IconSlotName | null {
|
|
102
107
|
if (!isValidElement(node)) return null;
|
|
103
108
|
|
|
104
109
|
return ((node.type as IconSlotComponent)[iconSlotMarker] ?? null) as IconSlotName | null;
|
|
105
110
|
}
|
|
106
111
|
|
|
112
|
+
function isMulticolorIcon(node: React.ReactNode): boolean {
|
|
113
|
+
if (!isValidElement(node)) return false;
|
|
114
|
+
if (node.type == null || (typeof node.type !== "function" && typeof node.type !== "object")) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return (node.type as MulticolorIconComponent)[seedMulticolorIconMarker] === true;
|
|
119
|
+
}
|
|
120
|
+
|
|
107
121
|
////////////////////////////////////////////////////////////////////////////////////
|
|
108
122
|
|
|
109
123
|
export interface IconProps extends LynxStyledElementProps {
|
|
110
124
|
icon: ReactElement<LynxIconElementProps>;
|
|
111
125
|
size?: StyleProps["height"] | number;
|
|
112
126
|
color?: StyleProps["color"];
|
|
127
|
+
/**
|
|
128
|
+
* 자동 감지되지 않는 멀티컬러 아이콘의 원본 색상을 유지합니다.
|
|
129
|
+
* @default false
|
|
130
|
+
*/
|
|
131
|
+
multicolor?: boolean;
|
|
113
132
|
}
|
|
114
133
|
|
|
115
134
|
export interface PrefixIconProps extends IconProps {}
|
|
@@ -151,6 +170,7 @@ const IconSlotBase = React.forwardRef<unknown, IconSlotBaseProps>((props, ref) =
|
|
|
151
170
|
style,
|
|
152
171
|
size,
|
|
153
172
|
color,
|
|
173
|
+
multicolor = false,
|
|
154
174
|
children: _children,
|
|
155
175
|
...nativeProps
|
|
156
176
|
} = props;
|
|
@@ -159,9 +179,10 @@ const IconSlotBase = React.forwardRef<unknown, IconSlotBaseProps>((props, ref) =
|
|
|
159
179
|
const sourceRef = useMainThreadRef<MainThread.Element>(null);
|
|
160
180
|
const slotClassName = slot != null ? context?.classNames?.[slot] : undefined;
|
|
161
181
|
const styleColor = getStyleColor(style);
|
|
182
|
+
const shouldPreserveOriginalColor = multicolor || isMulticolorIcon(icon);
|
|
162
183
|
const iconColor = useIconColor(
|
|
163
184
|
[baseClassName, slotClassName, className, size, color, styleColor, ...(context?.deps ?? [])],
|
|
164
|
-
{ sourceRef },
|
|
185
|
+
{ sourceRef, enabled: !shouldPreserveOriginalColor },
|
|
165
186
|
);
|
|
166
187
|
const hasValidIcon = isValidElement<LynxIconElementProps>(icon);
|
|
167
188
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { getQueriesForElement, render } from "@lynx-js/react/testing-library";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
import { MannerTemp, MannerTempEmote } from "./MannerTemp";
|
|
6
|
+
|
|
7
|
+
function getRenderedRoot() {
|
|
8
|
+
const root = elementTree.root;
|
|
9
|
+
|
|
10
|
+
if (!root) {
|
|
11
|
+
throw new Error("Expected Lynx render root to exist.");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return root;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("MannerTemp", () => {
|
|
18
|
+
it("renders the label and emote with the selected level", () => {
|
|
19
|
+
render(
|
|
20
|
+
<MannerTemp className="custom-manner-temp" level="l6">
|
|
21
|
+
40°C
|
|
22
|
+
<MannerTempEmote />
|
|
23
|
+
</MannerTemp>,
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const root = getRenderedRoot();
|
|
27
|
+
const { getByText } = getQueriesForElement(root);
|
|
28
|
+
const mannerTempRoot = root.querySelector(".seed-manner-temp__root");
|
|
29
|
+
const label = getByText("40°C");
|
|
30
|
+
const emote = root.querySelector(".seed-manner-temp__emote");
|
|
31
|
+
|
|
32
|
+
expect(mannerTempRoot).toHaveClass("custom-manner-temp");
|
|
33
|
+
expect(mannerTempRoot).toHaveClass("seed-manner-temp__root--level_l6");
|
|
34
|
+
expect(label).toHaveClass("seed-manner-temp__label");
|
|
35
|
+
expect(label).toHaveClass("seed-manner-temp__label--level_l6");
|
|
36
|
+
expect(emote).toHaveAttribute(
|
|
37
|
+
"src",
|
|
38
|
+
expect.stringContaining("bf8f9b4d-c72e-4bf2-a094-460d3ad1b11f"),
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { mannerTemp, type MannerTempVariantProps } from "@seed-design/lynx-css/recipes/manner-temp";
|
|
2
|
+
import type { NodesRef } from "@lynx-js/types";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import * as React from "@lynx-js/react";
|
|
5
|
+
import { isValidElement } from "@lynx-js/react";
|
|
6
|
+
|
|
7
|
+
import type { LynxStyledElementProps, LynxViewRef } from "../../types";
|
|
8
|
+
import { toArray } from "../../utils/children";
|
|
9
|
+
|
|
10
|
+
type MannerTempLevel = NonNullable<MannerTempVariantProps["level"]>;
|
|
11
|
+
|
|
12
|
+
const emoteSources = {
|
|
13
|
+
l1: "https://assetstorage.krrt.io/1123529253537884138/b63c9b3c-410c-4cf5-ba83-d787a03c3c57/width_96_height_96.webp",
|
|
14
|
+
l2: "https://assetstorage.krrt.io/1123529253537884138/8e4bc458-f6d4-41ce-bfa2-17ea34022f5b/width_96_height_96.webp",
|
|
15
|
+
l3: "https://assetstorage.krrt.io/1123529253537884138/906ff501-edf6-402d-a3a4-a93ce9e04d30/width_96_height_96.webp",
|
|
16
|
+
l4: "https://assetstorage.krrt.io/1123529253537884138/10753976-b25d-4e4c-84b2-dbbe196dd0d9/width_96_height_96.webp",
|
|
17
|
+
l5: "https://assetstorage.krrt.io/1123529253537884138/ddfbd296-5089-408c-aa37-919f45074e5e/width_96_height_96.webp",
|
|
18
|
+
l6: "https://assetstorage.krrt.io/1123529253537884138/bf8f9b4d-c72e-4bf2-a094-460d3ad1b11f/width_96_height_96.webp",
|
|
19
|
+
l7: "https://assetstorage.krrt.io/1123529253537884138/9906a459-a4e3-4521-a6e5-0cc24c0aa763/width_96_height_96.webp",
|
|
20
|
+
l8: "https://assetstorage.krrt.io/1123529253537884138/438edadb-d31d-4711-aebd-a72f303fdf49/width_96_height_96.webp",
|
|
21
|
+
l9: "https://assetstorage.krrt.io/1123529253537884138/61f6c297-11da-4d72-ba90-20cd69c09c22/width_96_height_96.webp",
|
|
22
|
+
l10: "https://assetstorage.krrt.io/1123529253537884138/6cc410ac-3a55-4542-b49f-53551db74c4d/width_96_height_96.webp",
|
|
23
|
+
} satisfies Record<MannerTempLevel, string>;
|
|
24
|
+
|
|
25
|
+
const MannerTempLevelContext = React.createContext<MannerTempLevel | null>(null);
|
|
26
|
+
|
|
27
|
+
export interface MannerTempEmoteProps extends LynxStyledElementProps {
|
|
28
|
+
level?: MannerTempLevel;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const MannerTempEmote = React.forwardRef<unknown, MannerTempEmoteProps>((props, ref) => {
|
|
32
|
+
const contextLevel = React.useContext(MannerTempLevelContext);
|
|
33
|
+
const { level = contextLevel ?? "l1", children: _children, className, ...nativeProps } = props;
|
|
34
|
+
const classes = mannerTemp({ level });
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<image
|
|
38
|
+
{...(ref ? { ref: ref as React.Ref<NodesRef> } : {})}
|
|
39
|
+
{...nativeProps}
|
|
40
|
+
src={emoteSources[level]}
|
|
41
|
+
mode="aspectFit"
|
|
42
|
+
accessibility-elements-hidden={true}
|
|
43
|
+
className={clsx(classes.emote, className)}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
MannerTempEmote.displayName = "MannerTempEmote";
|
|
49
|
+
|
|
50
|
+
function isMannerTempEmote(node: React.ReactNode) {
|
|
51
|
+
return isValidElement(node) && node.type === MannerTempEmote;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @platform Lynx
|
|
56
|
+
*
|
|
57
|
+
* React와 같은 `level` variant와 `MannerTempEmote` 조합을 제공합니다.
|
|
58
|
+
* HTML `<span>` 대신 native `<view>` / `<text>` / `<image>`를 렌더링합니다.
|
|
59
|
+
*/
|
|
60
|
+
export interface MannerTempProps extends MannerTempVariantProps, LynxStyledElementProps {}
|
|
61
|
+
|
|
62
|
+
export const MannerTemp = React.forwardRef<unknown, MannerTempProps>((props, ref) => {
|
|
63
|
+
const [variantProps, otherProps] = mannerTemp.splitVariantProps(props);
|
|
64
|
+
const classes = mannerTemp(variantProps);
|
|
65
|
+
const { children, className, ...nativeProps } = otherProps;
|
|
66
|
+
const childArray = toArray(children);
|
|
67
|
+
const emoteChildren = childArray.filter(isMannerTempEmote);
|
|
68
|
+
const labelChildren = childArray.filter((child) => !isMannerTempEmote(child));
|
|
69
|
+
const level = variantProps.level ?? "l1";
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<MannerTempLevelContext.Provider value={level}>
|
|
73
|
+
<view
|
|
74
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
75
|
+
{...nativeProps}
|
|
76
|
+
className={clsx(classes.root, className)}
|
|
77
|
+
>
|
|
78
|
+
<text className={classes.label}>{labelChildren}</text>
|
|
79
|
+
{emoteChildren}
|
|
80
|
+
</view>
|
|
81
|
+
</MannerTempLevelContext.Provider>
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
MannerTemp.displayName = "MannerTemp";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./MannerTemp";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { getQueriesForElement, render } from "@lynx-js/react/testing-library";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
import { MannerTempBadge } from "./MannerTempBadge";
|
|
6
|
+
|
|
7
|
+
function getRenderedRoot() {
|
|
8
|
+
const root = elementTree.root;
|
|
9
|
+
|
|
10
|
+
if (!root) {
|
|
11
|
+
throw new Error("Expected Lynx render root to exist.");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return root;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("MannerTempBadge", () => {
|
|
18
|
+
it("renders the label with the selected level and preserves className", () => {
|
|
19
|
+
render(
|
|
20
|
+
<MannerTempBadge className="custom-manner-temp-badge" level="l10">
|
|
21
|
+
80°C
|
|
22
|
+
</MannerTempBadge>,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const root = getRenderedRoot();
|
|
26
|
+
const { getByText } = getQueriesForElement(root);
|
|
27
|
+
const badgeRoot = root.querySelector(".seed-manner-temp-badge__root");
|
|
28
|
+
const label = getByText("80°C");
|
|
29
|
+
|
|
30
|
+
expect(badgeRoot).toHaveClass("custom-manner-temp-badge");
|
|
31
|
+
expect(badgeRoot).toHaveClass("seed-manner-temp-badge__root--level_l10");
|
|
32
|
+
expect(label).toHaveClass("seed-manner-temp-badge__label");
|
|
33
|
+
expect(label).toHaveClass("seed-manner-temp-badge__label--level_l10");
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mannerTempBadge,
|
|
3
|
+
type MannerTempBadgeVariantProps,
|
|
4
|
+
} from "@seed-design/lynx-css/recipes/manner-temp-badge";
|
|
5
|
+
import clsx from "clsx";
|
|
6
|
+
import * as React from "@lynx-js/react";
|
|
7
|
+
|
|
8
|
+
import type { LynxStyledElementProps, LynxViewRef } from "../../types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @platform Lynx
|
|
12
|
+
*
|
|
13
|
+
* React와 같은 `level` variant를 제공하지만, HTML `<span>` 대신 native
|
|
14
|
+
* `<view>` / `<text>`를 렌더링합니다.
|
|
15
|
+
*/
|
|
16
|
+
export interface MannerTempBadgeProps extends MannerTempBadgeVariantProps, LynxStyledElementProps {}
|
|
17
|
+
|
|
18
|
+
export const MannerTempBadge = React.forwardRef<unknown, MannerTempBadgeProps>((props, ref) => {
|
|
19
|
+
const [variantProps, otherProps] = mannerTempBadge.splitVariantProps(props);
|
|
20
|
+
const classes = mannerTempBadge(variantProps);
|
|
21
|
+
const { children, className, ...nativeProps } = otherProps;
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<view
|
|
25
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
26
|
+
{...nativeProps}
|
|
27
|
+
className={clsx(classes.root, className)}
|
|
28
|
+
>
|
|
29
|
+
<text className={classes.label}>{children}</text>
|
|
30
|
+
</view>
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
MannerTempBadge.displayName = "MannerTempBadge";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./MannerTempBadge";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render } from "@lynx-js/react/testing-library";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
import { NotificationBadge, NotificationBadgePositioner } from "./NotificationBadge";
|
|
6
|
+
|
|
7
|
+
describe("NotificationBadge", () => {
|
|
8
|
+
it("inherits the positioner's size and preserves user class names", () => {
|
|
9
|
+
render(
|
|
10
|
+
<view>
|
|
11
|
+
<NotificationBadgePositioner attach="text" size="small" className="custom-positioner">
|
|
12
|
+
<NotificationBadge className="custom-badge" />
|
|
13
|
+
</NotificationBadgePositioner>
|
|
14
|
+
</view>,
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const positioner = elementTree.root?.querySelector(".seed-notification-badge-positioner");
|
|
18
|
+
const badge = elementTree.root?.querySelector(".seed-notification-badge__root");
|
|
19
|
+
const label = elementTree.root?.querySelector(".seed-notification-badge__label");
|
|
20
|
+
|
|
21
|
+
expect(positioner).toHaveClass("custom-positioner");
|
|
22
|
+
expect(positioner).toHaveClass("seed-notification-badge-positioner--attach_text");
|
|
23
|
+
expect(positioner).toHaveClass("seed-notification-badge-positioner--size_small");
|
|
24
|
+
expect(badge).toHaveClass("custom-badge");
|
|
25
|
+
expect(badge).toHaveClass("seed-notification-badge__root--size_small");
|
|
26
|
+
expect(label).toHaveAttribute("text-single-line-vertical-align", "center");
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
notificationBadge,
|
|
3
|
+
type NotificationBadgeVariantProps,
|
|
4
|
+
} from "@seed-design/lynx-css/recipes/notification-badge";
|
|
5
|
+
import {
|
|
6
|
+
notificationBadgePositioner,
|
|
7
|
+
type NotificationBadgePositionerVariantProps,
|
|
8
|
+
} from "@seed-design/lynx-css/recipes/notification-badge-positioner";
|
|
9
|
+
import clsx from "clsx";
|
|
10
|
+
import * as React from "@lynx-js/react";
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
LynxAccessibilityProps,
|
|
14
|
+
LynxStyledElementProps,
|
|
15
|
+
LynxViewRef,
|
|
16
|
+
} from "../../types";
|
|
17
|
+
|
|
18
|
+
const NotificationBadgeContext = React.createContext<NotificationBadgeVariantProps | null>(null);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @platform Lynx
|
|
22
|
+
*
|
|
23
|
+
* 웹 Notification Badge와 동일한 `size` variant를 제공하며, Lynx native `<view>`와
|
|
24
|
+
* `<text>`를 직접 렌더링합니다. `NotificationBadgePositioner` 안에서는 Positioner의 `size`를
|
|
25
|
+
* 기본값으로 사용하고, Badge에 직접 전달한 `size`가 이를 덮어씁니다.
|
|
26
|
+
*/
|
|
27
|
+
export interface NotificationBadgeProps
|
|
28
|
+
extends NotificationBadgeVariantProps,
|
|
29
|
+
LynxStyledElementProps,
|
|
30
|
+
LynxAccessibilityProps {}
|
|
31
|
+
|
|
32
|
+
export const NotificationBadge = React.forwardRef<unknown, NotificationBadgeProps>(
|
|
33
|
+
(innerProps, ref) => {
|
|
34
|
+
const contextProps = React.useContext(NotificationBadgeContext);
|
|
35
|
+
const props = { ...contextProps, ...innerProps };
|
|
36
|
+
const [variantProps, otherProps] = notificationBadge.splitVariantProps(props);
|
|
37
|
+
const classes = notificationBadge(variantProps);
|
|
38
|
+
const { children, className, ...nativeProps } = otherProps;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<view
|
|
42
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
43
|
+
{...nativeProps}
|
|
44
|
+
className={clsx(classes.root, className)}
|
|
45
|
+
>
|
|
46
|
+
<text className={classes.label} text-single-line-vertical-align="center">
|
|
47
|
+
{children}
|
|
48
|
+
</text>
|
|
49
|
+
</view>
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
NotificationBadge.displayName = "NotificationBadge";
|
|
55
|
+
|
|
56
|
+
export interface NotificationBadgePositionerProps
|
|
57
|
+
extends NotificationBadgePositionerVariantProps,
|
|
58
|
+
LynxStyledElementProps {}
|
|
59
|
+
|
|
60
|
+
export const NotificationBadgePositioner = React.forwardRef<
|
|
61
|
+
unknown,
|
|
62
|
+
NotificationBadgePositionerProps
|
|
63
|
+
>((props, ref) => {
|
|
64
|
+
const [variantProps, otherProps] = notificationBadgePositioner.splitVariantProps(props);
|
|
65
|
+
const { children, className, ...nativeProps } = otherProps;
|
|
66
|
+
const contextValue = React.useMemo(() => ({ size: variantProps.size }), [variantProps.size]);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<NotificationBadgeContext.Provider value={contextValue}>
|
|
70
|
+
<view
|
|
71
|
+
{...(ref ? { ref: ref as LynxViewRef } : {})}
|
|
72
|
+
{...nativeProps}
|
|
73
|
+
className={clsx(notificationBadgePositioner(variantProps), className)}
|
|
74
|
+
>
|
|
75
|
+
{children}
|
|
76
|
+
</view>
|
|
77
|
+
</NotificationBadgeContext.Provider>
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
NotificationBadgePositioner.displayName = "NotificationBadgePositioner";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./NotificationBadge";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export {
|
|
2
|
+
PageBannerBody as Body,
|
|
3
|
+
PageBannerButton as Button,
|
|
4
|
+
PageBannerCloseButton as CloseButton,
|
|
5
|
+
PageBannerContent as Content,
|
|
6
|
+
PageBannerDescription as Description,
|
|
7
|
+
PageBannerRoot as Root,
|
|
8
|
+
PageBannerTitle as Title,
|
|
9
|
+
type PageBannerBodyProps as BodyProps,
|
|
10
|
+
type PageBannerButtonProps as ButtonProps,
|
|
11
|
+
type PageBannerCloseButtonProps as CloseButtonProps,
|
|
12
|
+
type PageBannerContentProps as ContentProps,
|
|
13
|
+
type PageBannerDescriptionProps as DescriptionProps,
|
|
14
|
+
type PageBannerRootProps as RootProps,
|
|
15
|
+
type PageBannerTitleProps as TitleProps,
|
|
16
|
+
} from "./PageBanner";
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { fireEvent, render } from "@lynx-js/react/testing-library";
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
|
|
5
|
+
import * as PageBanner from "./PageBanner.namespace";
|
|
6
|
+
|
|
7
|
+
function getRenderedRoot() {
|
|
8
|
+
const root = elementTree.root;
|
|
9
|
+
|
|
10
|
+
if (!root) {
|
|
11
|
+
throw new Error("Expected Lynx render root to exist.");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return root;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getPageBannerRoot() {
|
|
18
|
+
const root = getRenderedRoot();
|
|
19
|
+
|
|
20
|
+
if (root.classList.contains("seed-page-banner__root")) return root;
|
|
21
|
+
|
|
22
|
+
const pageBannerRoot = root.querySelector<HTMLElement>(".seed-page-banner__root");
|
|
23
|
+
if (!pageBannerRoot) {
|
|
24
|
+
throw new Error("Expected PageBanner root to exist.");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return pageBannerRoot;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("PageBanner", () => {
|
|
31
|
+
it("renders compound slots with the default neutral weak style", () => {
|
|
32
|
+
render(
|
|
33
|
+
<PageBanner.Root>
|
|
34
|
+
<PageBanner.Content>
|
|
35
|
+
<PageBanner.Body>
|
|
36
|
+
<PageBanner.Title>Title</PageBanner.Title>
|
|
37
|
+
<PageBanner.Description>Description</PageBanner.Description>
|
|
38
|
+
</PageBanner.Body>
|
|
39
|
+
<PageBanner.Button>Action</PageBanner.Button>
|
|
40
|
+
</PageBanner.Content>
|
|
41
|
+
<PageBanner.CloseButton accessibility-label="Close" />
|
|
42
|
+
</PageBanner.Root>,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const root = getPageBannerRoot();
|
|
46
|
+
|
|
47
|
+
expect(root).toHaveClass("seed-page-banner__root--tone_neutral-variant_weak");
|
|
48
|
+
expect(root.querySelector(".seed-page-banner__content")?.tagName.toLowerCase()).toBe("view");
|
|
49
|
+
expect(root.querySelector(".seed-page-banner__body")?.tagName.toLowerCase()).toBe("text");
|
|
50
|
+
expect(root.querySelector(".seed-page-banner__title")).toHaveClass(
|
|
51
|
+
"seed-page-banner__title--tone_neutral-variant_weak",
|
|
52
|
+
);
|
|
53
|
+
expect(root.querySelector(".seed-page-banner__description")).toHaveClass(
|
|
54
|
+
"seed-page-banner__description--tone_neutral-variant_weak",
|
|
55
|
+
);
|
|
56
|
+
expect(root.querySelector(".seed-page-banner__button")).toHaveAttribute(
|
|
57
|
+
"accessibility-traits",
|
|
58
|
+
"button",
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("tracks pressed state and maps actionable accessibility", () => {
|
|
63
|
+
const onTap = vi.fn();
|
|
64
|
+
|
|
65
|
+
render(
|
|
66
|
+
<PageBanner.Root tone="positive" variant="solid" bindtap={onTap}>
|
|
67
|
+
<PageBanner.Content>
|
|
68
|
+
<PageBanner.Body>
|
|
69
|
+
<PageBanner.Description>Actionable</PageBanner.Description>
|
|
70
|
+
</PageBanner.Body>
|
|
71
|
+
</PageBanner.Content>
|
|
72
|
+
</PageBanner.Root>,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const root = getPageBannerRoot();
|
|
76
|
+
|
|
77
|
+
expect(root).toHaveAttribute("accessibility-element", "true");
|
|
78
|
+
expect(root).toHaveAttribute("accessibility-traits", "button");
|
|
79
|
+
|
|
80
|
+
fireEvent.touchstart(root, {});
|
|
81
|
+
expect(root).toHaveClass("seed-page-banner__root--tone_positive-variant_solid-pressed_true");
|
|
82
|
+
|
|
83
|
+
fireEvent.tap(root);
|
|
84
|
+
expect(onTap).toHaveBeenCalledTimes(1);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("dismisses an uncontrolled banner and composes the close tap handler", () => {
|
|
88
|
+
const onTap = vi.fn();
|
|
89
|
+
const onDismiss = vi.fn();
|
|
90
|
+
|
|
91
|
+
render(
|
|
92
|
+
<PageBanner.Root onDismiss={onDismiss}>
|
|
93
|
+
<PageBanner.CloseButton bindtap={onTap} accessibility-label="Close" />
|
|
94
|
+
</PageBanner.Root>,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const closeButton = getPageBannerRoot().querySelector(".seed-page-banner__closeButton");
|
|
98
|
+
if (!closeButton) throw new Error("Expected close button to exist.");
|
|
99
|
+
|
|
100
|
+
fireEvent.tap(closeButton);
|
|
101
|
+
|
|
102
|
+
expect(onTap).toHaveBeenCalledTimes(1);
|
|
103
|
+
expect(onDismiss).toHaveBeenCalledTimes(1);
|
|
104
|
+
expect(getRenderedRoot().querySelector(".seed-page-banner__root")).not.toBeInTheDocument();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("notifies without hiding a controlled banner", () => {
|
|
108
|
+
const onDismiss = vi.fn();
|
|
109
|
+
|
|
110
|
+
render(
|
|
111
|
+
<PageBanner.Root open onDismiss={onDismiss}>
|
|
112
|
+
<PageBanner.CloseButton accessibility-label="Close" />
|
|
113
|
+
</PageBanner.Root>,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const closeButton = getPageBannerRoot().querySelector(".seed-page-banner__closeButton");
|
|
117
|
+
if (!closeButton) throw new Error("Expected close button to exist.");
|
|
118
|
+
|
|
119
|
+
fireEvent.tap(closeButton);
|
|
120
|
+
|
|
121
|
+
expect(onDismiss).toHaveBeenCalledTimes(1);
|
|
122
|
+
expect(getPageBannerRoot()).toBeInTheDocument();
|
|
123
|
+
});
|
|
124
|
+
});
|