@telefonica/mistica 16.56.0 → 16.56.1-beta.1
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/README.md +31 -0
- package/css/mistica.css +1 -1
- package/dist/accordion.css-mistica.js +16 -16
- package/dist/align.css-mistica.js +2 -2
- package/dist/autocomplete.css-mistica.js +6 -6
- package/dist/avatar.css-mistica.js +3 -3
- package/dist/badge.css-mistica.js +7 -7
- package/dist/box.css-mistica.js +15 -15
- package/dist/boxed.css-mistica.js +31 -31
- package/dist/button-group.css-mistica.js +10 -10
- package/dist/button-layout.css-mistica.js +21 -21
- package/dist/button.css-mistica.js +51 -51
- package/dist/callout.css-mistica.js +16 -16
- package/dist/card-internal.css-mistica.js +36 -36
- package/dist/carousel.css-mistica.js +18 -18
- package/dist/checkbox.css-mistica.js +21 -21
- package/dist/chip.css-mistica.js +30 -30
- package/dist/circle.css-mistica.js +2 -2
- package/dist/community/advanced-data-card.css-mistica.js +26 -26
- package/dist/community/blocks.css-mistica.js +3 -3
- package/dist/community/example-component.css-mistica.js +2 -2
- package/dist/counter.css-mistica.js +12 -12
- package/dist/cover-hero.css-mistica.js +16 -16
- package/dist/credit-card-number-field.css-mistica.js +6 -6
- package/dist/date-field.css-mistica.js +1 -1
- package/dist/date-time-picker.css-mistica.js +2 -2
- package/dist/dialog.css-mistica.js +15 -15
- package/dist/divider.css-mistica.js +5 -5
- package/dist/double-field.css-mistica.js +4 -4
- package/dist/drawer.css-mistica.js +15 -15
- package/dist/empty-state-card.css-mistica.js +4 -4
- package/dist/empty-state.css-mistica.js +14 -14
- package/dist/fade-in.css-mistica.js +1 -1
- package/dist/feedback.css-mistica.js +14 -14
- package/dist/file-upload.css-mistica.js +14 -14
- package/dist/fixed-footer-layout.css-mistica.js +12 -12
- package/dist/form.css-mistica.js +2 -2
- package/dist/grid-layout.css-mistica.js +9 -9
- package/dist/grid.css-mistica.js +147 -147
- package/dist/header.css-mistica.js +5 -5
- package/dist/hero.css-mistica.js +11 -11
- package/dist/horizontal-scroll.css-mistica.js +3 -3
- package/dist/icon-button.css-mistica.js +66 -66
- package/dist/icons/icon-chevron.css-mistica.js +6 -6
- package/dist/icons/icon-error.css-mistica.js +3 -3
- package/dist/image.css-mistica.js +11 -11
- package/dist/inline.css-mistica.js +16 -16
- package/dist/list.css-mistica.js +15 -15
- package/dist/loading-bar.css-mistica.js +5 -5
- package/dist/loading-screen.css-mistica.js +15 -15
- package/dist/logo-blau-shell.d.ts +10 -0
- package/dist/logo-blau-shell.js +25 -0
- package/dist/logo-blau.d.ts +1 -1
- package/dist/logo-blau.js +25 -40
- package/dist/logo-common.d.ts +0 -1
- package/dist/logo-esimflag-shell.d.ts +10 -0
- package/dist/logo-esimflag-shell.js +25 -0
- package/dist/logo-esimflag.d.ts +1 -1
- package/dist/logo-esimflag.js +11 -26
- package/dist/logo-movistar-new-shell.d.ts +10 -0
- package/dist/logo-movistar-new-shell.js +25 -0
- package/dist/logo-movistar-new.d.ts +1 -1
- package/dist/logo-movistar-new.js +84 -103
- package/dist/logo-movistar-shell.d.ts +10 -0
- package/dist/logo-movistar-shell.js +25 -0
- package/dist/logo-movistar.d.ts +1 -1
- package/dist/logo-movistar.js +16 -31
- package/dist/logo-o2-new-shell.d.ts +8 -0
- package/dist/logo-o2-new-shell.js +24 -0
- package/dist/logo-o2-new.d.ts +1 -1
- package/dist/logo-o2-new.js +6 -13
- package/dist/logo-o2-shell.d.ts +8 -0
- package/dist/logo-o2-shell.js +24 -0
- package/dist/logo-o2.d.ts +1 -1
- package/dist/logo-o2.js +6 -13
- package/dist/logo-telefonica-shell.d.ts +10 -0
- package/dist/logo-telefonica-shell.js +25 -0
- package/dist/logo-telefonica.d.ts +1 -1
- package/dist/logo-telefonica.js +11 -26
- package/dist/logo-tu-shell.d.ts +8 -0
- package/dist/logo-tu-shell.js +24 -0
- package/dist/logo-tu.d.ts +1 -1
- package/dist/logo-tu.js +10 -14
- package/dist/logo-vivo-shell.d.ts +10 -0
- package/dist/logo-vivo-shell.js +25 -0
- package/dist/logo-vivo.d.ts +1 -1
- package/dist/logo-vivo.js +11 -26
- package/dist/logo.css-mistica.js +9 -9
- package/dist/logo.js +223 -205
- package/dist/menu.css-mistica.js +24 -24
- package/dist/mosaic.css-mistica.js +3 -3
- package/dist/navigation-bar.css-mistica.js +45 -45
- package/dist/navigation-breadcrumbs.css-mistica.js +5 -5
- package/dist/package-version.js +2 -2
- package/dist/pin-field.css-mistica.js +10 -10
- package/dist/popover.css-mistica.js +2 -2
- package/dist/progress-bar.css-mistica.js +12 -12
- package/dist/radio-button.css-mistica.js +33 -33
- package/dist/rating.css-mistica.js +12 -12
- package/dist/responsive-layout.css-mistica.js +20 -20
- package/dist/screen-reader-only.css-mistica.js +2 -2
- package/dist/select.css-mistica.js +29 -29
- package/dist/sheet-action-row.css-mistica.js +2 -2
- package/dist/sheet-common.css-mistica.js +16 -16
- package/dist/sheet-info.css-mistica.js +4 -4
- package/dist/skeletons.css-mistica.js +12 -12
- package/dist/skins/skin-contract.css-mistica.js +686 -686
- package/dist/skip-link.css-mistica.js +3 -3
- package/dist/slider.css-mistica.js +28 -28
- package/dist/snackbar.css-mistica.js +16 -16
- package/dist/spinner.css-mistica.js +5 -5
- package/dist/square.css-mistica.js +2 -2
- package/dist/stack.css-mistica.js +9 -9
- package/dist/stacking-group.css-mistica.js +2 -2
- package/dist/stepper.css-mistica.js +16 -16
- package/dist/switch-component.css-mistica.js +53 -53
- package/dist/table.css-mistica.js +24 -24
- package/dist/tabs.css-mistica.js +30 -30
- package/dist/tag.css-mistica.js +5 -5
- package/dist/text-field-base.css-mistica.js +30 -30
- package/dist/text-field-base.js +52 -51
- package/dist/text-field-components.css-mistica.js +20 -17
- package/dist/text-field-components.css.d.ts +1 -0
- package/dist/text-field-components.d.ts +2 -1
- package/dist/text-field-components.js +25 -25
- package/dist/text-link.css-mistica.js +10 -10
- package/dist/text.css-mistica.js +13 -13
- package/dist/theme-context.css-mistica.js +2 -2
- package/dist/timeline.css-mistica.js +18 -18
- package/dist/timer.css-mistica.js +13 -13
- package/dist/tooltip.css-mistica.js +12 -12
- package/dist/touchable.css-mistica.js +5 -5
- package/dist/utils/aspect-ratio-support.css-mistica.js +7 -7
- package/dist/video.css-mistica.js +2 -2
- package/dist/vivinho-loading-animation/vivinho-loading-animation.css-mistica.js +3 -3
- package/dist-es/accordion.css-mistica.js +7 -7
- package/dist-es/align.css-mistica.js +2 -2
- package/dist-es/autocomplete.css-mistica.js +2 -2
- package/dist-es/avatar.css-mistica.js +2 -2
- package/dist-es/badge.css-mistica.js +2 -2
- package/dist-es/box.css-mistica.js +15 -15
- package/dist-es/boxed.css-mistica.js +25 -25
- package/dist-es/button-group.css-mistica.js +2 -2
- package/dist-es/button-layout.css-mistica.js +16 -16
- package/dist-es/button.css-mistica.js +38 -38
- package/dist-es/callout.css-mistica.js +12 -12
- package/dist-es/card-internal.css-mistica.js +20 -20
- package/dist-es/carousel.css-mistica.js +10 -10
- package/dist-es/checkbox.css-mistica.js +14 -14
- package/dist-es/chip.css-mistica.js +17 -17
- package/dist-es/circle.css-mistica.js +2 -2
- package/dist-es/community/advanced-data-card.css-mistica.js +7 -7
- package/dist-es/community/blocks.css-mistica.js +2 -2
- package/dist-es/community/example-component.css-mistica.js +2 -2
- package/dist-es/counter.css-mistica.js +2 -2
- package/dist-es/cover-hero.css-mistica.js +4 -4
- package/dist-es/credit-card-number-field.css-mistica.js +4 -4
- package/dist-es/date-field.css-mistica.js +1 -1
- package/dist-es/date-time-picker.css-mistica.js +2 -2
- package/dist-es/dialog.css-mistica.js +5 -5
- package/dist-es/divider.css-mistica.js +5 -5
- package/dist-es/double-field.css-mistica.js +4 -4
- package/dist-es/drawer.css-mistica.js +2 -2
- package/dist-es/empty-state-card.css-mistica.js +2 -2
- package/dist-es/empty-state.css-mistica.js +7 -7
- package/dist-es/fade-in.css-mistica.js +1 -1
- package/dist-es/feedback.css-mistica.js +2 -2
- package/dist-es/file-upload.css-mistica.js +8 -8
- package/dist-es/fixed-footer-layout.css-mistica.js +4 -4
- package/dist-es/form.css-mistica.js +2 -2
- package/dist-es/grid-layout.css-mistica.js +4 -4
- package/dist-es/grid.css-mistica.js +127 -127
- package/dist-es/header.css-mistica.js +2 -2
- package/dist-es/hero.css-mistica.js +4 -4
- package/dist-es/horizontal-scroll.css-mistica.js +2 -2
- package/dist-es/icon-button.css-mistica.js +56 -56
- package/dist-es/icons/icon-chevron.css-mistica.js +4 -4
- package/dist-es/icons/icon-error.css-mistica.js +2 -2
- package/dist-es/image.css-mistica.js +4 -4
- package/dist-es/inline.css-mistica.js +10 -10
- package/dist-es/list.css-mistica.js +2 -2
- package/dist-es/loading-bar.css-mistica.js +2 -2
- package/dist-es/loading-screen.css-mistica.js +5 -5
- package/dist-es/logo-blau-shell.js +16 -0
- package/dist-es/logo-blau.js +36 -51
- package/dist-es/logo-esimflag-shell.js +16 -0
- package/dist-es/logo-esimflag.js +13 -28
- package/dist-es/logo-movistar-new-shell.js +16 -0
- package/dist-es/logo-movistar-new.js +86 -105
- package/dist-es/logo-movistar-shell.js +16 -0
- package/dist-es/logo-movistar.js +18 -33
- package/dist-es/logo-o2-new-shell.js +15 -0
- package/dist-es/logo-o2-new.js +7 -14
- package/dist-es/logo-o2-shell.js +15 -0
- package/dist-es/logo-o2.js +7 -14
- package/dist-es/logo-telefonica-shell.js +16 -0
- package/dist-es/logo-telefonica.js +13 -28
- package/dist-es/logo-tu-shell.js +15 -0
- package/dist-es/logo-tu.js +12 -16
- package/dist-es/logo-vivo-shell.js +16 -0
- package/dist-es/logo-vivo.js +13 -28
- package/dist-es/logo.css-mistica.js +7 -7
- package/dist-es/logo.js +221 -203
- package/dist-es/menu.css-mistica.js +15 -15
- package/dist-es/mosaic.css-mistica.js +2 -2
- package/dist-es/navigation-bar.css-mistica.js +20 -20
- package/dist-es/navigation-breadcrumbs.css-mistica.js +2 -2
- package/dist-es/package-version.js +2 -2
- package/dist-es/pin-field.css-mistica.js +2 -2
- package/dist-es/popover.css-mistica.js +2 -2
- package/dist-es/progress-bar.css-mistica.js +8 -8
- package/dist-es/radio-button.css-mistica.js +25 -25
- package/dist-es/rating.css-mistica.js +4 -4
- package/dist-es/responsive-layout.css-mistica.js +7 -7
- package/dist-es/screen-reader-only.css-mistica.js +2 -2
- package/dist-es/select.css-mistica.js +20 -20
- package/dist-es/sheet-action-row.css-mistica.js +2 -2
- package/dist-es/sheet-common.css-mistica.js +2 -2
- package/dist-es/sheet-info.css-mistica.js +2 -2
- package/dist-es/skeletons.css-mistica.js +8 -8
- package/dist-es/skins/skin-contract.css-mistica.js +686 -686
- package/dist-es/skip-link.css-mistica.js +2 -2
- package/dist-es/slider.css-mistica.js +20 -20
- package/dist-es/snackbar.css-mistica.js +5 -5
- package/dist-es/spinner.css-mistica.js +2 -2
- package/dist-es/square.css-mistica.js +2 -2
- package/dist-es/stack.css-mistica.js +7 -7
- package/dist-es/stacking-group.css-mistica.js +2 -2
- package/dist-es/stepper.css-mistica.js +4 -4
- package/dist-es/style.css +1 -1
- package/dist-es/switch-component.css-mistica.js +41 -41
- package/dist-es/table.css-mistica.js +11 -11
- package/dist-es/tabs.css-mistica.js +21 -21
- package/dist-es/tag.css-mistica.js +2 -2
- package/dist-es/text-field-base.css-mistica.js +17 -17
- package/dist-es/text-field-base.js +52 -51
- package/dist-es/text-field-components.css-mistica.js +4 -4
- package/dist-es/text-field-components.js +52 -52
- package/dist-es/text-link.css-mistica.js +9 -9
- package/dist-es/text.css-mistica.js +8 -8
- package/dist-es/theme-context.css-mistica.js +2 -2
- package/dist-es/timeline.css-mistica.js +11 -11
- package/dist-es/timer.css-mistica.js +7 -7
- package/dist-es/tooltip.css-mistica.js +3 -3
- package/dist-es/touchable.css-mistica.js +2 -2
- package/dist-es/utils/aspect-ratio-support.css-mistica.js +4 -4
- package/dist-es/video.css-mistica.js +2 -2
- package/dist-es/vivinho-loading-animation/vivinho-loading-animation.css-mistica.js +2 -2
- package/doc/analytics.md +145 -0
- package/doc/components.md +730 -0
- package/doc/design-tokens.md +199 -0
- package/doc/fonts.md +169 -0
- package/doc/forms.md +177 -0
- package/doc/images/layout/box.svg +6 -0
- package/doc/images/layout/grid-layout-desktop-5-4.svg +8 -0
- package/doc/images/layout/grid-layout-desktop-6-6.svg +8 -0
- package/doc/images/layout/grid-layout-desktop-8-4.svg +8 -0
- package/doc/images/layout/grid-layout-desktop.svg +16 -0
- package/doc/images/layout/grid-layout-mobile-5-4.svg +9 -0
- package/doc/images/layout/grid-layout-mobile-6-6.svg +9 -0
- package/doc/images/layout/grid-layout-mobile-8-4.svg +9 -0
- package/doc/images/layout/grid-layout-mobile.svg +5 -0
- package/doc/images/layout/grid-layout-tablet-5-4.svg +8 -0
- package/doc/images/layout/grid-layout-tablet-6-6.svg +8 -0
- package/doc/images/layout/grid-layout-tablet-8-4.svg +8 -0
- package/doc/images/layout/grid-layout-tablet.svg +5 -0
- package/doc/images/layout/header-layout-desktop.svg +6 -0
- package/doc/images/layout/header-layout-mobile.svg +6 -0
- package/doc/images/layout/header-layout-tablet.svg +6 -0
- package/doc/images/layout/inline-around.svg +6 -0
- package/doc/images/layout/inline-between.svg +6 -0
- package/doc/images/layout/inline-evenly.svg +6 -0
- package/doc/images/layout/inline.svg +6 -0
- package/doc/images/layout/master-detail-layout-desktop.svg +8 -0
- package/doc/images/layout/master-detail-layout-mobile-detail.svg +6 -0
- package/doc/images/layout/master-detail-layout-mobile-master.svg +6 -0
- package/doc/images/layout/negative-box-ok-outline.svg +17 -0
- package/doc/images/layout/negative-box-ok-preview.svg +11 -0
- package/doc/images/layout/negative-box-wrong-outline.svg +14 -0
- package/doc/images/layout/negative-box-wrong-preview.svg +11 -0
- package/doc/images/layout/responsive-layout-desktop.svg +4 -0
- package/doc/images/layout/responsive-layout-mobile.svg +4 -0
- package/doc/images/layout/responsive-layout-tablet.svg +4 -0
- package/doc/images/layout/stack.svg +8 -0
- package/doc/images/layout/vertical-rhythm.png +0 -0
- package/doc/layout.md +527 -0
- package/doc/llms.md +258 -0
- package/doc/lottie.md +17 -0
- package/doc/migration-guide.md +76 -0
- package/doc/patterns.md +546 -0
- package/doc/sheet.md +122 -0
- package/doc/testing.md +43 -0
- package/doc/texts.md +42 -0
- package/doc/theme-config.md +124 -0
- package/package.json +2 -1
package/doc/patterns.md
ADDED
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
# Patterns and Best Practices
|
|
2
|
+
|
|
3
|
+
## Critical rules
|
|
4
|
+
|
|
5
|
+
1. **NEVER hardcode colors.** Always use `skinVars.colors.*` from `@telefonica/mistica`.
|
|
6
|
+
2. **NEVER use raw `<div>` for layout.** Use `Box`, `Stack`, `Inline`, `ResponsiveLayout`, `GridLayout`,
|
|
7
|
+
`Grid`.
|
|
8
|
+
3. **NEVER set font sizes manually.** Use text components (`Text1`-`Text10`, `Title1`-`Title4`).
|
|
9
|
+
4. **NEVER set border radius manually.** Use `skinVars.borderRadii.*` or components that handle it (`Boxed`,
|
|
10
|
+
cards, etc.).
|
|
11
|
+
5. **Always wrap your app** with `ThemeContextProvider` and import `@telefonica/mistica/css/mistica.css`.
|
|
12
|
+
6. **Always namespace React hooks**: `React.useState`, `React.useEffect`, `React.useRef`, etc.
|
|
13
|
+
7. **Add `'use client';`** directive to client components when using Next.js app router.
|
|
14
|
+
|
|
15
|
+
## Page layout composition
|
|
16
|
+
|
|
17
|
+
A standard Mistica page follows this structure:
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
// Navigation
|
|
21
|
+
<MainNavigationBar ... />
|
|
22
|
+
|
|
23
|
+
// Header section
|
|
24
|
+
<HeaderLayout
|
|
25
|
+
header={<Header pretitle="Section" title="Page Title" description="Description" />}
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
// Content sections
|
|
29
|
+
<ResponsiveLayout>
|
|
30
|
+
<Box paddingY={24}>
|
|
31
|
+
<Stack space={32}>
|
|
32
|
+
{/* Section 1 */}
|
|
33
|
+
<Stack space={16}>
|
|
34
|
+
<Title1 as="h2">Section Title</Title1>
|
|
35
|
+
<Text2 regular as="p">Section description</Text2>
|
|
36
|
+
</Stack>
|
|
37
|
+
|
|
38
|
+
{/* Section 2 - List */}
|
|
39
|
+
<Stack space={16}>
|
|
40
|
+
<Title1 as="h2">Another Section</Title1>
|
|
41
|
+
<NegativeBox>
|
|
42
|
+
<RowList>
|
|
43
|
+
<Row title="Item 1" onPress={() => {}} />
|
|
44
|
+
<Row title="Item 2" onPress={() => {}} />
|
|
45
|
+
</RowList>
|
|
46
|
+
</NegativeBox>
|
|
47
|
+
</Stack>
|
|
48
|
+
</Stack>
|
|
49
|
+
</Box>
|
|
50
|
+
</ResponsiveLayout>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Vertical rhythm
|
|
54
|
+
|
|
55
|
+
Follow the 24/32/16 rule:
|
|
56
|
+
|
|
57
|
+
- **Containers**: 24px top and bottom padding (`<Box paddingY={24}>`)
|
|
58
|
+
- **Sections**: 32px space between them (`<Stack space={32}>`)
|
|
59
|
+
- **Elements**: 16px space between them (`<Stack space={16}>`)
|
|
60
|
+
|
|
61
|
+
## Layout dos and don'ts
|
|
62
|
+
|
|
63
|
+
### DO: Use layout components
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
// Vertical spacing
|
|
67
|
+
<Stack space={16}>
|
|
68
|
+
<Text2 regular>First</Text2>
|
|
69
|
+
<Text2 regular>Second</Text2>
|
|
70
|
+
</Stack>
|
|
71
|
+
|
|
72
|
+
// Horizontal spacing
|
|
73
|
+
<Inline space={16}>
|
|
74
|
+
<ButtonPrimary onPress={() => {}}>Accept</ButtonPrimary>
|
|
75
|
+
<ButtonSecondary onPress={() => {}}>Cancel</ButtonSecondary>
|
|
76
|
+
</Inline>
|
|
77
|
+
|
|
78
|
+
// Padding
|
|
79
|
+
<Box padding={16}>
|
|
80
|
+
<Text2 regular>Padded content</Text2>
|
|
81
|
+
</Box>
|
|
82
|
+
|
|
83
|
+
// Page container
|
|
84
|
+
<ResponsiveLayout>
|
|
85
|
+
<Text2 regular>Responsive content</Text2>
|
|
86
|
+
</ResponsiveLayout>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### DON'T: Use divs for spacing/layout
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
// BAD - raw divs for spacing
|
|
93
|
+
<div style={{display: 'flex', flexDirection: 'column', gap: 16}}>
|
|
94
|
+
<div style={{padding: 16}}>
|
|
95
|
+
<span style={{fontSize: 14, color: '#333'}}>Text</span>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
// GOOD - Mistica components
|
|
100
|
+
<Stack space={16}>
|
|
101
|
+
<Box padding={16}>
|
|
102
|
+
<Text2 regular>Text</Text2>
|
|
103
|
+
</Box>
|
|
104
|
+
</Stack>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Color dos and don'ts
|
|
108
|
+
|
|
109
|
+
### DO: Use design tokens
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
import {skinVars, applyAlpha} from '@telefonica/mistica';
|
|
113
|
+
|
|
114
|
+
// Direct token usage
|
|
115
|
+
<Text2 regular color={skinVars.colors.textSecondary}>Secondary text</Text2>
|
|
116
|
+
|
|
117
|
+
// In styles (when absolutely needed)
|
|
118
|
+
<div style={{backgroundColor: skinVars.colors.backgroundContainer}}>...</div>
|
|
119
|
+
|
|
120
|
+
// Semi-transparent colors
|
|
121
|
+
const overlay = applyAlpha(skinVars.rawColors.backgroundBrand, 0.8);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### DON'T: Hardcode colors
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
// BAD - hardcoded colors
|
|
128
|
+
<Text2 regular color="#666">Text</Text2>
|
|
129
|
+
<div style={{backgroundColor: '#f5f5f5'}}>...</div>
|
|
130
|
+
<div style={{color: 'rgb(0, 112, 224)'}}>...</div>
|
|
131
|
+
|
|
132
|
+
// GOOD - design tokens
|
|
133
|
+
<Text2 regular color={skinVars.colors.textSecondary}>Text</Text2>
|
|
134
|
+
<Boxed><Text2 regular>Content in container</Text2></Boxed>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Responsive patterns
|
|
138
|
+
|
|
139
|
+
### Conditional rendering by screen size
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
const {isDesktopOrBigger, isTabletOrSmaller} = useScreenSize();
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<ResponsiveLayout>
|
|
146
|
+
<Box paddingY={24}>
|
|
147
|
+
{isDesktopOrBigger ? (
|
|
148
|
+
<GridLayout template="6+6" left={<LeftContent />} right={<RightContent />} />
|
|
149
|
+
) : (
|
|
150
|
+
<Stack space={16}>
|
|
151
|
+
<LeftContent />
|
|
152
|
+
<RightContent />
|
|
153
|
+
</Stack>
|
|
154
|
+
)}
|
|
155
|
+
</Box>
|
|
156
|
+
</ResponsiveLayout>
|
|
157
|
+
);
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Grid templates for desktop/mobile
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
<ResponsiveLayout>
|
|
164
|
+
<GridLayout template="8+4" left={<MainContent />} right={<Sidebar />} />
|
|
165
|
+
</ResponsiveLayout>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
On mobile, GridLayout stacks content vertically automatically.
|
|
169
|
+
|
|
170
|
+
### Master-detail pattern
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
const [selectedId, setSelectedId] = React.useState(null);
|
|
174
|
+
|
|
175
|
+
<MasterDetailLayout
|
|
176
|
+
isOpen={!!selectedId}
|
|
177
|
+
master={
|
|
178
|
+
<RowList>
|
|
179
|
+
{items.map((item) => (
|
|
180
|
+
<Row key={item.id} title={item.name} onPress={() => setSelectedId(item.id)} />
|
|
181
|
+
))}
|
|
182
|
+
</RowList>
|
|
183
|
+
}
|
|
184
|
+
detail={selectedId ? <DetailView id={selectedId} /> : <Text2 regular>Select an item</Text2>}
|
|
185
|
+
/>;
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Form patterns
|
|
189
|
+
|
|
190
|
+
### Automatic state management (preferred)
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
<Form initialValues={{email: '', name: ''}} onSubmit={(formData) => api.submit(formData)}>
|
|
194
|
+
<Stack space={16}>
|
|
195
|
+
<TextField name="name" label="Name" />
|
|
196
|
+
<EmailField name="email" label="Email" />
|
|
197
|
+
<PhoneNumberField name="phone" label="Phone" optional />
|
|
198
|
+
<DoubleField>
|
|
199
|
+
<DateField name="startDate" label="Start date" />
|
|
200
|
+
<DateField name="endDate" label="End date" />
|
|
201
|
+
</DoubleField>
|
|
202
|
+
<Select
|
|
203
|
+
name="country"
|
|
204
|
+
label="Country"
|
|
205
|
+
options={[
|
|
206
|
+
{value: 'es', text: 'Spain'},
|
|
207
|
+
{value: 'uk', text: 'United Kingdom'},
|
|
208
|
+
]}
|
|
209
|
+
/>
|
|
210
|
+
<Switch name="newsletter">Receive newsletter</Switch>
|
|
211
|
+
<ButtonLayout
|
|
212
|
+
primaryButton={
|
|
213
|
+
<ButtonPrimary submit loadingText="Sending...">
|
|
214
|
+
Submit
|
|
215
|
+
</ButtonPrimary>
|
|
216
|
+
}
|
|
217
|
+
/>
|
|
218
|
+
</Stack>
|
|
219
|
+
</Form>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Form with fixed footer
|
|
223
|
+
|
|
224
|
+
```tsx
|
|
225
|
+
<ButtonFixedFooterLayout button={<ButtonPrimary submit>Continue</ButtonPrimary>}>
|
|
226
|
+
<ResponsiveLayout>
|
|
227
|
+
<Box paddingY={24}>
|
|
228
|
+
<Form onSubmit={handleSubmit}>
|
|
229
|
+
<Stack space={16}>
|
|
230
|
+
<TextField name="name" label="Name" />
|
|
231
|
+
<EmailField name="email" label="Email" />
|
|
232
|
+
</Stack>
|
|
233
|
+
</Form>
|
|
234
|
+
</Box>
|
|
235
|
+
</ResponsiveLayout>
|
|
236
|
+
</ButtonFixedFooterLayout>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Carousel patterns
|
|
240
|
+
|
|
241
|
+
`Carousel`, `CenteredCarousel`, and `Slideshow` are **horizontal-scroll** components. Use them when content
|
|
242
|
+
needs to be navigated left-to-right (e.g. product cards, promotional banners, image galleries).
|
|
243
|
+
|
|
244
|
+
### Carousel vs CenteredCarousel vs Slideshow
|
|
245
|
+
|
|
246
|
+
| Component | Best for | Visible items |
|
|
247
|
+
| ------------------ | -------------------------------------------------- | --------------------------- |
|
|
248
|
+
| `Carousel` | Card collections, product grids | Configurable per breakpoint |
|
|
249
|
+
| `CenteredCarousel` | Featured/highlighted items with neighbour peek | 1 on mobile, 3 on desktop |
|
|
250
|
+
| `Slideshow` | Full-width hero banners, autoplay image slideshows | 1 at a time |
|
|
251
|
+
|
|
252
|
+
### Placement rules
|
|
253
|
+
|
|
254
|
+
- **`Carousel` and `CenteredCarousel`**: place inside `ResponsiveLayout` so they respect the page grid.
|
|
255
|
+
- **`Slideshow`**: place **outside** `ResponsiveLayout` — it bleeds full-width on mobile and tablet
|
|
256
|
+
automatically.
|
|
257
|
+
|
|
258
|
+
```tsx
|
|
259
|
+
{
|
|
260
|
+
/* Carousel inside ResponsiveLayout */
|
|
261
|
+
}
|
|
262
|
+
<ResponsiveLayout>
|
|
263
|
+
<Box paddingY={24}>
|
|
264
|
+
<Stack space={16}>
|
|
265
|
+
<Title2 as="h2">Featured products</Title2>
|
|
266
|
+
<Carousel
|
|
267
|
+
itemsPerPage={{mobile: 1, tablet: 2, desktop: 3}}
|
|
268
|
+
withBullets
|
|
269
|
+
items={products.map((p, i) => (
|
|
270
|
+
<DataCard
|
|
271
|
+
key={i}
|
|
272
|
+
title={p.name}
|
|
273
|
+
description={p.description}
|
|
274
|
+
buttonPrimary={
|
|
275
|
+
<ButtonPrimary small onPress={() => navigate(p.url)}>
|
|
276
|
+
View
|
|
277
|
+
</ButtonPrimary>
|
|
278
|
+
}
|
|
279
|
+
/>
|
|
280
|
+
))}
|
|
281
|
+
/>
|
|
282
|
+
</Stack>
|
|
283
|
+
</Box>
|
|
284
|
+
</ResponsiveLayout>;
|
|
285
|
+
|
|
286
|
+
{
|
|
287
|
+
/* Slideshow at page level (outside ResponsiveLayout) */
|
|
288
|
+
}
|
|
289
|
+
<Slideshow
|
|
290
|
+
withBullets
|
|
291
|
+
autoplay={{time: 5000, loop: true}}
|
|
292
|
+
items={[
|
|
293
|
+
<CoverCard key="1" imageSrc="/banner1.jpg" title="Offer 1" />,
|
|
294
|
+
<CoverCard key="2" imageSrc="/banner2.jpg" title="Offer 2" />,
|
|
295
|
+
]}
|
|
296
|
+
/>;
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Card patterns
|
|
300
|
+
|
|
301
|
+
### Asset pattern for cards and rows
|
|
302
|
+
|
|
303
|
+
The idiomatic way to create card/row assets is `Circle` + icon:
|
|
304
|
+
|
|
305
|
+
```tsx
|
|
306
|
+
<Circle backgroundColor={skinVars.colors.brandLow} size={40}>
|
|
307
|
+
<IconShopRegular color={skinVars.colors.brand} />
|
|
308
|
+
</Circle>
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Card grid
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
<ResponsiveLayout>
|
|
315
|
+
<Box paddingY={24}>
|
|
316
|
+
<Stack space={16}>
|
|
317
|
+
<Title1 as="h2">Featured</Title1>
|
|
318
|
+
<Carousel
|
|
319
|
+
itemsPerPage={{mobile: 1, tablet: 2, desktop: 3}}
|
|
320
|
+
items={products.map((p) => (
|
|
321
|
+
<DataCard
|
|
322
|
+
key={p.id}
|
|
323
|
+
asset={
|
|
324
|
+
<Circle backgroundColor={skinVars.colors.brandLow} size={40}>
|
|
325
|
+
<IconShopRegular color={skinVars.colors.brand} />
|
|
326
|
+
</Circle>
|
|
327
|
+
}
|
|
328
|
+
title={p.name}
|
|
329
|
+
description={p.description}
|
|
330
|
+
buttonPrimary={
|
|
331
|
+
<ButtonPrimary small onPress={() => navigate(p.url)}>
|
|
332
|
+
View
|
|
333
|
+
</ButtonPrimary>
|
|
334
|
+
}
|
|
335
|
+
/>
|
|
336
|
+
))}
|
|
337
|
+
/>
|
|
338
|
+
</Stack>
|
|
339
|
+
</Box>
|
|
340
|
+
</ResponsiveLayout>
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## List patterns
|
|
344
|
+
|
|
345
|
+
### Unbounded list with NegativeBox
|
|
346
|
+
|
|
347
|
+
When placing a `RowList` inside a `ResponsiveLayout`, wrap it with `NegativeBox` so hover effects and
|
|
348
|
+
alignment are correct:
|
|
349
|
+
|
|
350
|
+
```tsx
|
|
351
|
+
<ResponsiveLayout>
|
|
352
|
+
<Box paddingY={24}>
|
|
353
|
+
<Stack space={16}>
|
|
354
|
+
<Title1>Settings</Title1>
|
|
355
|
+
<NegativeBox>
|
|
356
|
+
<RowList>
|
|
357
|
+
<Row
|
|
358
|
+
asset={
|
|
359
|
+
<Circle backgroundColor={skinVars.colors.brandLow} size={40}>
|
|
360
|
+
<IconSettingsRegular color={skinVars.colors.brand} />
|
|
361
|
+
</Circle>
|
|
362
|
+
}
|
|
363
|
+
title="General"
|
|
364
|
+
description="App settings"
|
|
365
|
+
onPress={() => {}}
|
|
366
|
+
/>
|
|
367
|
+
<Row
|
|
368
|
+
asset={
|
|
369
|
+
<Circle backgroundColor={skinVars.colors.brandLow} size={40}>
|
|
370
|
+
<IconBellRegular color={skinVars.colors.brand} />
|
|
371
|
+
</Circle>
|
|
372
|
+
}
|
|
373
|
+
title="Notifications"
|
|
374
|
+
description="Manage alerts"
|
|
375
|
+
onPress={() => {}}
|
|
376
|
+
/>
|
|
377
|
+
</RowList>
|
|
378
|
+
</NegativeBox>
|
|
379
|
+
</Stack>
|
|
380
|
+
</Box>
|
|
381
|
+
</ResponsiveLayout>
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Boxed list (no NegativeBox needed)
|
|
385
|
+
|
|
386
|
+
```tsx
|
|
387
|
+
<BoxedRowList>
|
|
388
|
+
<BoxedRow title="Option A" onPress={() => {}} />
|
|
389
|
+
<BoxedRow title="Option B" onPress={() => {}} />
|
|
390
|
+
</BoxedRowList>
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
## Variant sections
|
|
394
|
+
|
|
395
|
+
Use `variant` on `ResponsiveLayout` to create colored sections. Components inside adapt automatically:
|
|
396
|
+
|
|
397
|
+
```tsx
|
|
398
|
+
{
|
|
399
|
+
/* Default section */
|
|
400
|
+
}
|
|
401
|
+
<ResponsiveLayout>
|
|
402
|
+
<Box paddingY={24}>
|
|
403
|
+
<Text2 regular>Default background</Text2>
|
|
404
|
+
</Box>
|
|
405
|
+
</ResponsiveLayout>;
|
|
406
|
+
|
|
407
|
+
{
|
|
408
|
+
/* Brand section */
|
|
409
|
+
}
|
|
410
|
+
<ResponsiveLayout variant="brand" fullWidth>
|
|
411
|
+
<ResponsiveLayout>
|
|
412
|
+
<Box paddingY={24}>
|
|
413
|
+
<Stack space={16}>
|
|
414
|
+
<Title1>Brand Section</Title1>
|
|
415
|
+
<Text2 regular>Colors adapt automatically</Text2>
|
|
416
|
+
<ButtonPrimary onPress={() => {}}>Action</ButtonPrimary>
|
|
417
|
+
</Stack>
|
|
418
|
+
</Box>
|
|
419
|
+
</ResponsiveLayout>
|
|
420
|
+
</ResponsiveLayout>;
|
|
421
|
+
|
|
422
|
+
{
|
|
423
|
+
/* Alternative section */
|
|
424
|
+
}
|
|
425
|
+
<ResponsiveLayout variant="alternative" fullWidth>
|
|
426
|
+
<ResponsiveLayout>
|
|
427
|
+
<Box paddingY={24}>
|
|
428
|
+
<Text2 regular>Alternative background</Text2>
|
|
429
|
+
</Box>
|
|
430
|
+
</ResponsiveLayout>
|
|
431
|
+
</ResponsiveLayout>;
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
## Boxed containers
|
|
435
|
+
|
|
436
|
+
Use `Boxed` for card-like containers without card semantics:
|
|
437
|
+
|
|
438
|
+
```tsx
|
|
439
|
+
<Boxed>
|
|
440
|
+
<Box padding={16}>
|
|
441
|
+
<Stack space={16}>
|
|
442
|
+
<Title3>Container Title</Title3>
|
|
443
|
+
<Text2 regular>Container content</Text2>
|
|
444
|
+
</Stack>
|
|
445
|
+
</Box>
|
|
446
|
+
</Boxed>
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
## Skeleton loading patterns
|
|
450
|
+
|
|
451
|
+
Replace content with matching skeleton shapes while loading:
|
|
452
|
+
|
|
453
|
+
```tsx
|
|
454
|
+
const {data, isLoading} = useFetch('/api/data');
|
|
455
|
+
|
|
456
|
+
if (isLoading) {
|
|
457
|
+
return (
|
|
458
|
+
<Stack space={16}>
|
|
459
|
+
<SkeletonLine width="40%" />
|
|
460
|
+
<SkeletonText />
|
|
461
|
+
<Inline space={16}>
|
|
462
|
+
<SkeletonCircle size={40} />
|
|
463
|
+
<SkeletonLine width="60%" />
|
|
464
|
+
</Inline>
|
|
465
|
+
</Stack>
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return (
|
|
470
|
+
<Stack space={16}>
|
|
471
|
+
<Title1>{data.title}</Title1>
|
|
472
|
+
<Text2 regular>{data.description}</Text2>
|
|
473
|
+
<Inline space={16}>
|
|
474
|
+
<Avatar size={40} src={data.avatar} />
|
|
475
|
+
<Text2 regular>{data.author}</Text2>
|
|
476
|
+
</Inline>
|
|
477
|
+
</Stack>
|
|
478
|
+
);
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## Funnel / step-by-step flow
|
|
482
|
+
|
|
483
|
+
```tsx
|
|
484
|
+
<FunnelNavigationBar
|
|
485
|
+
right={
|
|
486
|
+
<NavigationBarActionGroup>
|
|
487
|
+
<NavigationBarAction aria-label="Close" onPress={handleClose}>
|
|
488
|
+
<IconCloseRegular color="currentColor" />
|
|
489
|
+
</NavigationBarAction>
|
|
490
|
+
</NavigationBarActionGroup>
|
|
491
|
+
}
|
|
492
|
+
/>
|
|
493
|
+
<Stepper currentIndex={currentStep} steps={['Personal', 'Address', 'Payment', 'Confirm']} />
|
|
494
|
+
<ResponsiveLayout>
|
|
495
|
+
<Box paddingY={24}>
|
|
496
|
+
{currentStep === 0 && <PersonalInfoForm />}
|
|
497
|
+
{currentStep === 1 && <AddressForm />}
|
|
498
|
+
{currentStep === 2 && <PaymentForm />}
|
|
499
|
+
{currentStep === 3 && <ConfirmationScreen />}
|
|
500
|
+
</Box>
|
|
501
|
+
</ResponsiveLayout>
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
## Next.js integration
|
|
505
|
+
|
|
506
|
+
### Link configuration
|
|
507
|
+
|
|
508
|
+
```tsx
|
|
509
|
+
import Link from 'next/link';
|
|
510
|
+
|
|
511
|
+
const theme = {
|
|
512
|
+
skin: getMovistarSkin(),
|
|
513
|
+
i18n: {locale: 'es-ES', phoneNumberFormattingRegionCode: 'ES'},
|
|
514
|
+
Link: {type: 'Next14', Component: Link},
|
|
515
|
+
};
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### React Router integration
|
|
519
|
+
|
|
520
|
+
```tsx
|
|
521
|
+
import {Link} from 'react-router-dom';
|
|
522
|
+
|
|
523
|
+
const theme = {
|
|
524
|
+
skin: getMovistarSkin(),
|
|
525
|
+
i18n: {locale: 'es-ES', phoneNumberFormattingRegionCode: 'ES'},
|
|
526
|
+
Link: {type: 'ReactRouter6', Component: Link},
|
|
527
|
+
};
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
After configuring, use the `to` prop on touchable components for client-side navigation:
|
|
531
|
+
|
|
532
|
+
```tsx
|
|
533
|
+
<ButtonPrimary to="/dashboard">Go to dashboard</ButtonPrimary>
|
|
534
|
+
<Row title="Profile" to="/profile" />
|
|
535
|
+
<TextLink to="/settings">Settings</TextLink>
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
## Dark mode
|
|
539
|
+
|
|
540
|
+
Mistica supports dark mode out of the box via `colorScheme` in theme config:
|
|
541
|
+
|
|
542
|
+
- `'auto'` (default) -- follows OS/browser preference
|
|
543
|
+
- `'light'` -- force light mode
|
|
544
|
+
- `'dark'` -- force dark mode
|
|
545
|
+
|
|
546
|
+
All `skinVars.colors.*` tokens automatically resolve to their dark mode values. No additional code is needed.
|
package/doc/sheet.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Sheet
|
|
2
|
+
|
|
3
|
+
Mística provides a sheet component that can be used to display a modal-like content from over the main content
|
|
4
|
+
of the screen.
|
|
5
|
+
|
|
6
|
+
## Predefined sheets
|
|
7
|
+
|
|
8
|
+
Some predefined sheets are available: `RadioListSheet`, `ActionsListSheet`, `InfoSheet` and `ActionsSheet`.
|
|
9
|
+
You can see examples in Storybook.
|
|
10
|
+
|
|
11
|
+
To use them, first you must configure the `SheetRoot` component in your app:
|
|
12
|
+
|
|
13
|
+
```jsx
|
|
14
|
+
import {SheetRoot} from '@telefonica/mistica';
|
|
15
|
+
|
|
16
|
+
export const App = () => {
|
|
17
|
+
return (
|
|
18
|
+
<SheetRoot>
|
|
19
|
+
<MyApplication />
|
|
20
|
+
</SheetRoot>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then you can call `showSheet` from anywhere:
|
|
26
|
+
|
|
27
|
+
```jsx
|
|
28
|
+
import {showSheet} from '@telefonica/mistica';
|
|
29
|
+
|
|
30
|
+
const MyComponent = () => {
|
|
31
|
+
return (
|
|
32
|
+
<ButtonPrimary
|
|
33
|
+
onPress={() =>
|
|
34
|
+
showSheet({
|
|
35
|
+
type: 'RADIO_LIST',
|
|
36
|
+
props: {
|
|
37
|
+
title: 'Select an fruit',
|
|
38
|
+
items: [
|
|
39
|
+
{id: '1', title: 'Apple'},
|
|
40
|
+
{id: '2', title: 'Banana'},
|
|
41
|
+
{id: '3', title: 'Orange'},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
}).then((result) => {
|
|
45
|
+
// The promise is resolved when the sheet is closed
|
|
46
|
+
console.log(result);
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
>
|
|
50
|
+
show sheet
|
|
51
|
+
</ButtonPrimary>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Native implementation
|
|
57
|
+
|
|
58
|
+
If your app is served inside a webview and uses the `webview-bridge` library, the native implementation of the
|
|
59
|
+
predefined sheets will be used.
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import {bottomSheet, isWebViewBridgeAvailable} from '@tef-novum/webview-bridge';
|
|
63
|
+
|
|
64
|
+
// ...
|
|
65
|
+
|
|
66
|
+
<SheetRoot nativeImplementation={isWebViewBridgeAvailable() ? bottomSheet : undefined}>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
When possible, always use the native implementation, as it provides a better user experience.
|
|
70
|
+
|
|
71
|
+
## Custom sheets
|
|
72
|
+
|
|
73
|
+
You can show any content you want inside the sheet by passing it as a child of the component.
|
|
74
|
+
|
|
75
|
+
```jsx
|
|
76
|
+
import {Sheet} from 'mistica';
|
|
77
|
+
|
|
78
|
+
const MyComponent = () => {
|
|
79
|
+
const [showSheet, setShowSheet] = useState(false);
|
|
80
|
+
return (
|
|
81
|
+
<>
|
|
82
|
+
<ButtonPrimary onPress={() => setShowSheet(true)}>show sheet</ButtonPrimary>
|
|
83
|
+
{showSheet && (
|
|
84
|
+
<Sheet onClose={() => setShowSheet(false)}>
|
|
85
|
+
<Placeholder />
|
|
86
|
+
</Sheet>
|
|
87
|
+
)}
|
|
88
|
+
</>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The sheet will close when the user does the swipe down gesture, when the background overlay is touched, or by
|
|
94
|
+
using the visible dismiss button (close icon) that appears in the top-right corner aligned with the title. The
|
|
95
|
+
`onClose` callback is called when the closing animation finishes, that's the right place to unmount the sheet
|
|
96
|
+
as shown in the example above.
|
|
97
|
+
|
|
98
|
+
You can also close the sheet programmatically using the render prop:
|
|
99
|
+
|
|
100
|
+
```jsx
|
|
101
|
+
import {Sheet} from 'mistica';
|
|
102
|
+
|
|
103
|
+
const MyComponent = () => {
|
|
104
|
+
const [showSheet, setShowSheet] = useState(false);
|
|
105
|
+
return (
|
|
106
|
+
<>
|
|
107
|
+
<ButtonPrimary onPress={() => setShowSheet(true)}>show sheet</ButtonPrimary>
|
|
108
|
+
{showSheet && (
|
|
109
|
+
<Sheet onClose={() => setShowSheet(false)}>
|
|
110
|
+
{({closeModal, modalTitleId}) => (
|
|
111
|
+
<>
|
|
112
|
+
<Title1 id={modalTitleId}>My sheet</Title1>
|
|
113
|
+
<Placeholder />
|
|
114
|
+
<ButtonPrimary onPress={closeModal}>Close</ButtonPrimary>
|
|
115
|
+
</>
|
|
116
|
+
)}
|
|
117
|
+
</Sheet>
|
|
118
|
+
)}
|
|
119
|
+
</>
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
```
|
package/doc/testing.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Testing notes
|
|
2
|
+
|
|
3
|
+
## Environment variables
|
|
4
|
+
|
|
5
|
+
Our code uses the NODE_ENV var to define code blocks that should be executed in specific environments, like
|
|
6
|
+
`'test'` or `'production'`.
|
|
7
|
+
|
|
8
|
+
For example:
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
12
|
+
// this will only be executed if not inside a test
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Make sure your bundler (webpack, rollup, parcel, etc) makes the correct substitutions and the minifier
|
|
17
|
+
effectively removes unused code.
|
|
18
|
+
|
|
19
|
+
## Unit tests
|
|
20
|
+
|
|
21
|
+
Unit tests usually don't require CSS because they test component functionality. Some Mistica components have
|
|
22
|
+
different layouts in mobile and desktop, and there are cases in which we use CSS styles to achieve this
|
|
23
|
+
result. If you are trying to test something that depends on these styles, you can either import `mistica.css`
|
|
24
|
+
classes in the HTML content you render in the test or write acceptance/screenshot tests instead.
|
|
25
|
+
|
|
26
|
+
## Acceptance tests
|
|
27
|
+
|
|
28
|
+
To change some behaviors to facilitate acceptance tests (tests that run in a browser), a helper function is
|
|
29
|
+
used.
|
|
30
|
+
|
|
31
|
+
`isRunningAcceptanceTest` returns true if the user agent includes the `'acceptance-test'` string.
|
|
32
|
+
|
|
33
|
+
To make this work in your test environment, you should add that string to the browser's user agent. For
|
|
34
|
+
example in `Puppeteer`:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
await page.setUserAgent(`${currentUserAgent} acceptance-test`);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Questions?
|
|
41
|
+
|
|
42
|
+
Don't hesistate to ask at
|
|
43
|
+
[Mistica Teams Channel](https://teams.microsoft.com/l/channel/19%3ad2e3607a32ec411b8bf492f43cd0fe0c%40thread.tacv2/General?groupId=e265fe99-929f-45d1-8154-699649674a40&tenantId=9744600e-3e04-492e-baa1-25ec245c6f10)
|