@telus-uds/components-web 1.9.0 → 1.11.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/CHANGELOG.md +40 -2
- package/lib/Breadcrumbs/Breadcrumbs.js +8 -3
- package/lib/Breadcrumbs/Item/Item.js +31 -6
- package/lib/Callout/Callout.js +24 -3
- package/lib/Disclaimer/Disclaimer.js +72 -0
- package/lib/Disclaimer/index.js +15 -0
- package/lib/Footnote/Footnote.js +70 -28
- package/lib/Footnote/FootnoteLink.js +11 -13
- package/lib/NavigationBar/NavigationBar.js +231 -0
- package/lib/NavigationBar/NavigationItem.js +111 -0
- package/lib/NavigationBar/NavigationSubMenu.js +179 -0
- package/lib/NavigationBar/collapseItems.js +51 -0
- package/lib/NavigationBar/index.js +13 -0
- package/lib/PriceLockup/PriceLockup.js +40 -17
- package/lib/PriceLockup/tokens.js +49 -116
- package/lib/Progress/ProgressBar.js +100 -0
- package/lib/Progress/index.js +16 -0
- package/lib/Ribbon/Ribbon.js +53 -32
- package/lib/Spinner/Spinner.js +18 -14
- package/lib/Table/Cell.js +15 -1
- package/lib/Toast/Toast.js +15 -8
- package/lib/VideoPicker/VideoPicker.js +177 -0
- package/lib/VideoPicker/VideoPickerPlayer.js +54 -0
- package/lib/VideoPicker/VideoPickerThumbnail.js +201 -0
- package/lib/VideoPicker/VideoSlider.js +100 -0
- package/lib/VideoPicker/index.js +13 -0
- package/lib/VideoPicker/videoPropType.js +25 -0
- package/lib/index.js +37 -1
- package/lib-module/Breadcrumbs/Breadcrumbs.js +8 -3
- package/lib-module/Breadcrumbs/Item/Item.js +32 -7
- package/lib-module/Callout/Callout.js +24 -3
- package/lib-module/Disclaimer/Disclaimer.js +54 -0
- package/lib-module/Disclaimer/index.js +1 -0
- package/lib-module/Footnote/Footnote.js +68 -27
- package/lib-module/Footnote/FootnoteLink.js +12 -14
- package/lib-module/NavigationBar/NavigationBar.js +207 -0
- package/lib-module/NavigationBar/NavigationItem.js +87 -0
- package/lib-module/NavigationBar/NavigationSubMenu.js +161 -0
- package/lib-module/NavigationBar/collapseItems.js +43 -0
- package/lib-module/NavigationBar/index.js +2 -0
- package/lib-module/PriceLockup/PriceLockup.js +42 -19
- package/lib-module/PriceLockup/tokens.js +54 -119
- package/lib-module/Progress/ProgressBar.js +83 -0
- package/lib-module/Progress/index.js +4 -0
- package/lib-module/Ribbon/Ribbon.js +53 -32
- package/lib-module/Spinner/Spinner.js +17 -14
- package/lib-module/Table/Cell.js +15 -1
- package/lib-module/Toast/Toast.js +15 -8
- package/lib-module/VideoPicker/VideoPicker.js +151 -0
- package/lib-module/VideoPicker/VideoPickerPlayer.js +41 -0
- package/lib-module/VideoPicker/VideoPickerThumbnail.js +180 -0
- package/lib-module/VideoPicker/VideoSlider.js +83 -0
- package/lib-module/VideoPicker/index.js +2 -0
- package/lib-module/VideoPicker/videoPropType.js +9 -0
- package/lib-module/index.js +4 -0
- package/package.json +3 -3
- package/src/Breadcrumbs/Breadcrumbs.jsx +4 -3
- package/src/Breadcrumbs/Item/Item.jsx +18 -4
- package/src/Callout/Callout.jsx +27 -3
- package/src/Disclaimer/Disclaimer.jsx +39 -0
- package/src/Disclaimer/index.js +1 -0
- package/src/Footnote/Footnote.jsx +76 -26
- package/src/Footnote/FootnoteLink.jsx +28 -18
- package/src/NavigationBar/NavigationBar.jsx +217 -0
- package/src/NavigationBar/NavigationItem.jsx +83 -0
- package/src/NavigationBar/NavigationSubMenu.jsx +121 -0
- package/src/NavigationBar/collapseItems.js +29 -0
- package/src/NavigationBar/index.js +3 -0
- package/src/PriceLockup/PriceLockup.jsx +47 -21
- package/src/PriceLockup/tokens.js +34 -54
- package/src/Progress/ProgressBar.jsx +67 -0
- package/src/Progress/index.js +6 -0
- package/src/Ribbon/Ribbon.jsx +21 -9
- package/src/Spinner/Spinner.jsx +20 -17
- package/src/Table/Cell.jsx +22 -5
- package/src/Toast/Toast.jsx +12 -5
- package/src/VideoPicker/VideoPicker.jsx +144 -0
- package/src/VideoPicker/VideoPickerPlayer.jsx +21 -0
- package/src/VideoPicker/VideoPickerThumbnail.jsx +182 -0
- package/src/VideoPicker/VideoSlider.jsx +85 -0
- package/src/VideoPicker/index.js +3 -0
- package/src/VideoPicker/videoPropType.js +12 -0
- package/src/index.js +4 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import React, { useRef } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import { Icon, Spacer, useResponsiveProp, useThemeTokens } from '@telus-uds/components-base'
|
|
4
|
+
import NavigationItem from './NavigationItem'
|
|
5
|
+
import Listbox from '../Listbox'
|
|
6
|
+
import useOverlaidPosition from '../utils/useOverlaidPosition'
|
|
7
|
+
import resolveItemSelection from './resolveItemSelection'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A NavigationItem that opens or closes a Listbox of other NavigationItems.
|
|
11
|
+
*
|
|
12
|
+
* This is rendered automatically by `NavigationBar` and isn't intended be used directly.
|
|
13
|
+
*/
|
|
14
|
+
const NavigationSubMenu = ({
|
|
15
|
+
children,
|
|
16
|
+
id,
|
|
17
|
+
isOpen = false,
|
|
18
|
+
label,
|
|
19
|
+
onClick,
|
|
20
|
+
selectedId,
|
|
21
|
+
items = [],
|
|
22
|
+
openOverlayRef,
|
|
23
|
+
LinkRouter,
|
|
24
|
+
linkRouterProps
|
|
25
|
+
}) => {
|
|
26
|
+
const focusTrapRef = useRef()
|
|
27
|
+
|
|
28
|
+
const maxWidth = 289 // Slightly over 288 of nav item to account for subpixel rounding
|
|
29
|
+
const { align, offsets, minWidth } = useResponsiveProp({
|
|
30
|
+
xs: { align: { top: 'top', left: 'left' }, minWidth: maxWidth },
|
|
31
|
+
sm: { align: { top: 'top', right: 'right' }, minWidth: maxWidth },
|
|
32
|
+
lg: {
|
|
33
|
+
align: { top: 'bottom', center: 'center' },
|
|
34
|
+
offsets: { vertical: 4 },
|
|
35
|
+
minWidth: 192
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const { overlaidPosition, sourceRef, targetRef, onTargetLayout, isReady } = useOverlaidPosition({
|
|
40
|
+
isShown: isOpen,
|
|
41
|
+
offsets,
|
|
42
|
+
align
|
|
43
|
+
})
|
|
44
|
+
const { selected } = resolveItemSelection({ id, label, items }, selectedId)
|
|
45
|
+
|
|
46
|
+
const handleClick = (event) => {
|
|
47
|
+
if (typeof onClick === 'function') onClick(event)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const { icoMenu } = useThemeTokens('NavigationBar', {}, {}, { expanded: isOpen })
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
<NavigationItem
|
|
55
|
+
ref={sourceRef}
|
|
56
|
+
accessibilityRole="button"
|
|
57
|
+
id={id}
|
|
58
|
+
selected={selected}
|
|
59
|
+
onClick={handleClick}
|
|
60
|
+
icon={icoMenu}
|
|
61
|
+
>
|
|
62
|
+
{({ textStyles }) => [
|
|
63
|
+
children,
|
|
64
|
+
<Spacer key={`${id}_spacer`} space={1} direction="row" />,
|
|
65
|
+
<Icon
|
|
66
|
+
key={`${id}_icon`}
|
|
67
|
+
icon={icoMenu}
|
|
68
|
+
variant={{ size: 'small' }}
|
|
69
|
+
tokens={{ color: textStyles[0]?.color, size: textStyles[0]?.fontSize }}
|
|
70
|
+
/>
|
|
71
|
+
]}
|
|
72
|
+
</NavigationItem>
|
|
73
|
+
{isOpen && (
|
|
74
|
+
<>
|
|
75
|
+
<Listbox.Overlay
|
|
76
|
+
overlaidPosition={overlaidPosition}
|
|
77
|
+
maxWidth={maxWidth}
|
|
78
|
+
minWidth={minWidth}
|
|
79
|
+
isReady={isReady}
|
|
80
|
+
onLayout={onTargetLayout}
|
|
81
|
+
ref={openOverlayRef}
|
|
82
|
+
>
|
|
83
|
+
<Listbox
|
|
84
|
+
items={items}
|
|
85
|
+
firstItemRef={targetRef}
|
|
86
|
+
parentRef={sourceRef}
|
|
87
|
+
selectedId={selectedId}
|
|
88
|
+
LinkRouter={LinkRouter}
|
|
89
|
+
linkRouterProps={linkRouterProps}
|
|
90
|
+
/>
|
|
91
|
+
</Listbox.Overlay>
|
|
92
|
+
<div
|
|
93
|
+
// This catches and shifts focus to other interactive elements.
|
|
94
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
95
|
+
tabIndex={0}
|
|
96
|
+
ref={focusTrapRef}
|
|
97
|
+
onFocus={() => targetRef.current.focus()}
|
|
98
|
+
/>
|
|
99
|
+
</>
|
|
100
|
+
)}
|
|
101
|
+
</>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
NavigationSubMenu.displayName = 'NavigationSubMenu'
|
|
106
|
+
|
|
107
|
+
// @TODO - proper prop types and comments
|
|
108
|
+
NavigationSubMenu.propTypes = {
|
|
109
|
+
children: PropTypes.node,
|
|
110
|
+
id: PropTypes.string,
|
|
111
|
+
isOpen: PropTypes.bool,
|
|
112
|
+
label: PropTypes.string,
|
|
113
|
+
onClick: PropTypes.func,
|
|
114
|
+
selectedId: PropTypes.string,
|
|
115
|
+
items: PropTypes.array,
|
|
116
|
+
openOverlayRef: PropTypes.object,
|
|
117
|
+
LinkRouter: PropTypes.elementType,
|
|
118
|
+
linkRouterProps: PropTypes.object
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export default NavigationSubMenu
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Make a list of items into a one-item list where all items are nested under the first item
|
|
3
|
+
*/
|
|
4
|
+
const collapseItems = (items, selectedId) => {
|
|
5
|
+
if (!items?.length) return items
|
|
6
|
+
|
|
7
|
+
// Give the root item the label of the current active link
|
|
8
|
+
// (or the first item if for some reason there's no match on the selectedId)
|
|
9
|
+
let rootLabel = items[0].label
|
|
10
|
+
const isSelected = ({ label, id }) => selectedId === id ?? label
|
|
11
|
+
|
|
12
|
+
// Linter doesn't like for loops, simulate loop that breaks
|
|
13
|
+
items.some((item) => {
|
|
14
|
+
if (isSelected(item)) {
|
|
15
|
+
rootLabel = item.label
|
|
16
|
+
return true // break
|
|
17
|
+
}
|
|
18
|
+
const nestedMatch = item.items?.find(isSelected)
|
|
19
|
+
if (nestedMatch) {
|
|
20
|
+
rootLabel = nestedMatch.label
|
|
21
|
+
return true // break
|
|
22
|
+
}
|
|
23
|
+
return false // continue
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
return [{ id: 'navigation-bar-root', label: rootLabel, items }]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default collapseItems
|
|
@@ -5,11 +5,12 @@ import {
|
|
|
5
5
|
Divider,
|
|
6
6
|
selectSystemProps,
|
|
7
7
|
Typography,
|
|
8
|
-
useThemeTokens
|
|
8
|
+
useThemeTokens,
|
|
9
|
+
useViewport
|
|
9
10
|
} from '@telus-uds/components-base'
|
|
10
11
|
import styled from 'styled-components'
|
|
11
12
|
import FootnoteLink from '../Footnote/FootnoteLink'
|
|
12
|
-
import
|
|
13
|
+
import getTypographyTokens from './tokens'
|
|
13
14
|
import { htmlAttrs, warn } from '../utils'
|
|
14
15
|
|
|
15
16
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs])
|
|
@@ -56,11 +57,23 @@ const StrikeThroughContainer = styled.div`
|
|
|
56
57
|
content: '';
|
|
57
58
|
width: 100%;
|
|
58
59
|
height: ${({ strikeThroughHeight }) => strikeThroughHeight};
|
|
59
|
-
background: ${({
|
|
60
|
+
background: ${({ strikeThroughColor }) => strikeThroughColor};
|
|
60
61
|
position: absolute;
|
|
61
62
|
}
|
|
62
63
|
`
|
|
63
64
|
|
|
65
|
+
const selectFootnoteLinkStyles = ({
|
|
66
|
+
footnoteLinkColor,
|
|
67
|
+
footnoteLinkFontName,
|
|
68
|
+
footnoteLinkFontWeight
|
|
69
|
+
}) => {
|
|
70
|
+
return {
|
|
71
|
+
color: footnoteLinkColor,
|
|
72
|
+
fontName: footnoteLinkFontName,
|
|
73
|
+
fontWeight: footnoteLinkFontWeight
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
64
77
|
const PriceLockup = ({
|
|
65
78
|
size = 'medium',
|
|
66
79
|
signDirection = 'left',
|
|
@@ -77,6 +90,7 @@ const PriceLockup = ({
|
|
|
77
90
|
variant = {},
|
|
78
91
|
...rest
|
|
79
92
|
}) => {
|
|
93
|
+
const viewport = useViewport()
|
|
80
94
|
const {
|
|
81
95
|
footnoteMarginTop,
|
|
82
96
|
footnoteGap,
|
|
@@ -85,11 +99,18 @@ const PriceLockup = ({
|
|
|
85
99
|
bottomLinksMarginLeft,
|
|
86
100
|
topTextMarginBottom,
|
|
87
101
|
strikeThroughHeight,
|
|
88
|
-
|
|
102
|
+
strikeThroughColor,
|
|
89
103
|
fontColor,
|
|
90
|
-
dividerColor
|
|
91
|
-
|
|
92
|
-
|
|
104
|
+
dividerColor,
|
|
105
|
+
footnoteLinkFontSize,
|
|
106
|
+
...themeTokens
|
|
107
|
+
} = useThemeTokens(
|
|
108
|
+
'PriceLockup',
|
|
109
|
+
priceLockupTokens,
|
|
110
|
+
{ ...variant, size },
|
|
111
|
+
{ viewport, strikeThrough }
|
|
112
|
+
)
|
|
113
|
+
const typographyTokens = getTypographyTokens(themeTokens)
|
|
93
114
|
const priceString = String(price)
|
|
94
115
|
const lastDotPosition = priceString.lastIndexOf('.')
|
|
95
116
|
const lastCommaPosition = priceString.lastIndexOf(',')
|
|
@@ -102,29 +123,32 @@ const PriceLockup = ({
|
|
|
102
123
|
const amount = hasCents ? priceString.substring(0, separatorPosition) : priceString
|
|
103
124
|
const cents = hasCents ? priceString.substring(separatorPosition + 1) : null
|
|
104
125
|
|
|
105
|
-
const color = strikeThrough ? strikeThroughBackground : fontColor
|
|
106
|
-
|
|
107
126
|
const renderTypography = (value, tokens) => (
|
|
108
|
-
<Typography tokens={{ ...tokens, color }}>{value}</Typography>
|
|
127
|
+
<Typography tokens={{ ...tokens, color: fontColor }}>{value}</Typography>
|
|
109
128
|
)
|
|
110
129
|
|
|
111
130
|
const renderCurrencySymbol = () =>
|
|
112
|
-
renderTypography(`${currencySymbol}`, typographyTokens.dollarSign
|
|
131
|
+
renderTypography(`${currencySymbol}`, typographyTokens.dollarSign)
|
|
113
132
|
|
|
114
133
|
const renderFootnoteLinks = () =>
|
|
115
134
|
footnoteLinks && footnoteLinks.length > 0 ? (
|
|
116
|
-
<FootnoteLink
|
|
135
|
+
<FootnoteLink
|
|
136
|
+
fontSize={footnoteLinkFontSize}
|
|
137
|
+
tokens={selectFootnoteLinkStyles(themeTokens)}
|
|
138
|
+
number={footnoteLinks}
|
|
139
|
+
onClick={onClickFootnote}
|
|
140
|
+
/>
|
|
117
141
|
) : null
|
|
118
142
|
|
|
119
143
|
const renderAmount = () => {
|
|
120
|
-
const amountComponent = renderTypography(amount, typographyTokens.amount
|
|
144
|
+
const amountComponent = renderTypography(amount, typographyTokens.amount)
|
|
121
145
|
if (strikeThrough) {
|
|
122
146
|
return (
|
|
123
147
|
<>
|
|
124
148
|
<A11yText text={a11yText} />
|
|
125
149
|
<StrikeThroughContainer
|
|
126
150
|
strikeThroughHeight={`${strikeThroughHeight}px`}
|
|
127
|
-
|
|
151
|
+
strikeThroughColor={strikeThroughColor}
|
|
128
152
|
>
|
|
129
153
|
{amountComponent}
|
|
130
154
|
</StrikeThroughContainer>
|
|
@@ -140,12 +164,10 @@ const PriceLockup = ({
|
|
|
140
164
|
<PriceContainer priceMarginBottom={`${priceMarginBottom}px`}>
|
|
141
165
|
{signDirection === 'left' && renderCurrencySymbol()}
|
|
142
166
|
{renderAmount()}
|
|
143
|
-
{cents && renderTypography(`${separator}${cents}`, typographyTokens.cents
|
|
167
|
+
{cents && renderTypography(`${separator}${cents}`, typographyTokens.cents)}
|
|
144
168
|
{signDirection === 'right' && <> {renderCurrencySymbol()}</>}
|
|
145
169
|
{rateText && (
|
|
146
|
-
<RateTextContainer>
|
|
147
|
-
{renderTypography(rateText, typographyTokens.rate[size])}
|
|
148
|
-
</RateTextContainer>
|
|
170
|
+
<RateTextContainer>{renderTypography(rateText, typographyTokens.rate)}</RateTextContainer>
|
|
149
171
|
)}
|
|
150
172
|
{!bottomText && footnoteLinks.length <= 3 && (
|
|
151
173
|
<BottomLinksContainer bottomLinksMarginLeft={`${bottomLinksMarginLeft}px`}>
|
|
@@ -164,7 +186,7 @@ const PriceLockup = ({
|
|
|
164
186
|
footnoteGap={`${footnoteGap}px`}
|
|
165
187
|
>
|
|
166
188
|
<BottomTextContainer bottomTextMarginTop={`${bottomTextMarginTop}px`}>
|
|
167
|
-
{renderTypography(bottomText, typographyTokens.bottomText
|
|
189
|
+
{renderTypography(bottomText, typographyTokens.bottomText)}
|
|
168
190
|
</BottomTextContainer>
|
|
169
191
|
{footnoteLinks.length <= 3 && renderFootnoteLinks()}
|
|
170
192
|
</FootnoteContainer>
|
|
@@ -180,12 +202,16 @@ const PriceLockup = ({
|
|
|
180
202
|
<PriceLockupContainer {...selectProps(rest)}>
|
|
181
203
|
{topText && (
|
|
182
204
|
<TopTextContainer topTextMarginBottom={`${topTextMarginBottom}px`}>
|
|
183
|
-
{renderTypography(topText, typographyTokens.topText
|
|
205
|
+
{renderTypography(topText, typographyTokens.topText)}
|
|
184
206
|
</TopTextContainer>
|
|
185
207
|
)}
|
|
186
208
|
{renderPrice()}
|
|
187
209
|
{bottomText && (
|
|
188
|
-
<Divider
|
|
210
|
+
<Divider
|
|
211
|
+
testID="price-lockup-divider"
|
|
212
|
+
role="separator"
|
|
213
|
+
tokens={{ color: dividerColor ?? 'rgba(0, 0, 0, 0)' }}
|
|
214
|
+
/>
|
|
189
215
|
)}
|
|
190
216
|
{bottomText && renderFootnoteContent()}
|
|
191
217
|
</PriceLockupContainer>
|
|
@@ -1,58 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
large: { fontSize: size20, lineHeight: ratio8to5 }
|
|
24
|
-
},
|
|
1
|
+
// map typography tokens accordenly
|
|
2
|
+
export default ({
|
|
3
|
+
topTextFontSize,
|
|
4
|
+
fontColor,
|
|
5
|
+
topTextLineHeight,
|
|
6
|
+
currencySymbolFontSize,
|
|
7
|
+
currencySymbolLineHeight,
|
|
8
|
+
currencySymbolFontWeight,
|
|
9
|
+
amountFontSize,
|
|
10
|
+
amountLineHeight,
|
|
11
|
+
amountLetterSpacing,
|
|
12
|
+
amountFontWeight,
|
|
13
|
+
centsFontSize,
|
|
14
|
+
centsLineHeight,
|
|
15
|
+
rateFontSize,
|
|
16
|
+
rateLineHeight,
|
|
17
|
+
bottomTextFontSize,
|
|
18
|
+
bottomTextLineHeight,
|
|
19
|
+
rateFontWeight,
|
|
20
|
+
centsFontWeight
|
|
21
|
+
}) => ({
|
|
22
|
+
topText: { fontSize: topTextFontSize, lineHeight: topTextLineHeight },
|
|
25
23
|
dollarSign: {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
fontSize: currencySymbolFontSize,
|
|
25
|
+
lineHeight: currencySymbolLineHeight,
|
|
26
|
+
fontWeight: currencySymbolFontWeight
|
|
29
27
|
},
|
|
30
|
-
// TODO - fontWeight should have its own const on palette
|
|
31
28
|
amount: {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
fontWeight: '300'
|
|
38
|
-
},
|
|
39
|
-
large: { fontSize: size64, lineHeight: ratio1to1, letterSpacing: condensed, fontWeight: '300' }
|
|
40
|
-
},
|
|
41
|
-
cents: {
|
|
42
|
-
small: { fontSize: size16, lineHeight: ratio4to3 },
|
|
43
|
-
medium: { fontSize: size20, lineHeight: ratio6to5 },
|
|
44
|
-
large: { fontSize: size36, lineHeight: ratio9to8 }
|
|
45
|
-
},
|
|
46
|
-
rate: {
|
|
47
|
-
small: { fontSize: size14, lineHeight: ratio8to7 },
|
|
48
|
-
medium: { fontSize: size16, lineHeight: ratio5to4 },
|
|
49
|
-
large: { fontSize: size20, lineHeight: ratio8to5 }
|
|
29
|
+
color: fontColor,
|
|
30
|
+
fontSize: amountFontSize,
|
|
31
|
+
lineHeight: amountLineHeight,
|
|
32
|
+
letterSpacing: amountLetterSpacing,
|
|
33
|
+
fontWeight: amountFontWeight
|
|
50
34
|
},
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export default typographyTokens
|
|
35
|
+
cents: { fontSize: centsFontSize, lineHeight: centsLineHeight, fontWeight: centsFontWeight },
|
|
36
|
+
rate: { fontSize: rateFontSize, lineHeight: rateLineHeight, fontWeight: rateFontWeight },
|
|
37
|
+
bottomText: { fontSize: bottomTextFontSize, lineHeight: bottomTextLineHeight }
|
|
38
|
+
})
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import {
|
|
4
|
+
getTokensPropType,
|
|
5
|
+
Progress,
|
|
6
|
+
selectSystemProps,
|
|
7
|
+
useThemeTokens,
|
|
8
|
+
a11yProps,
|
|
9
|
+
viewProps,
|
|
10
|
+
variantProp
|
|
11
|
+
} from '@telus-uds/components-base'
|
|
12
|
+
import styled from 'styled-components'
|
|
13
|
+
|
|
14
|
+
// Passes React Native-oriented system props through UDS Progress
|
|
15
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
16
|
+
|
|
17
|
+
const { Bar: ProgressBarBase } = Progress
|
|
18
|
+
const Gradient = styled.div.attrs({ 'data-testid': 'ProgressBar-Gradient' })(
|
|
19
|
+
({ percentage, gradient: { angle, stops, type } }) => ({
|
|
20
|
+
height: '100%',
|
|
21
|
+
// As per the design specs, we need to have the gradient expanding to 100% and being
|
|
22
|
+
// revealed by bar width, so we need to stretch it beyond the parent (progress element)
|
|
23
|
+
// to the full length of the progress bar
|
|
24
|
+
width: `${(100 * 100) / percentage}%`,
|
|
25
|
+
background: `${type}-gradient(${angle}deg, ${stops[0].color}, ${stops[1].color})`
|
|
26
|
+
})
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The `ProgressBar` is a visual representation of linear progression.
|
|
31
|
+
* It provides simple but important information at a quick glance.
|
|
32
|
+
* This is a Web-specific progress bar.
|
|
33
|
+
*
|
|
34
|
+
* ## Component API
|
|
35
|
+
*
|
|
36
|
+
* Use props and tokens from the base ProgressBar component. The difference is that the
|
|
37
|
+
* `gradient` is being used here to provide gradient filling.
|
|
38
|
+
*
|
|
39
|
+
*/
|
|
40
|
+
const ProgressBar = ({ percentage, tokens, variant, ...rest }) => {
|
|
41
|
+
const themeTokens = useThemeTokens('ProgressBar', tokens, variant)
|
|
42
|
+
const selectedProps = selectProps(rest)
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<ProgressBarBase percentage={percentage} tokens={tokens} variant={variant} {...selectedProps}>
|
|
46
|
+
{themeTokens.gradient && <Gradient {...themeTokens} percentage={percentage} />}
|
|
47
|
+
</ProgressBarBase>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
ProgressBar.propTypes = {
|
|
52
|
+
...selectedSystemPropTypes,
|
|
53
|
+
/**
|
|
54
|
+
* Percentage of completion.
|
|
55
|
+
*/
|
|
56
|
+
percentage: PropTypes.number,
|
|
57
|
+
/**
|
|
58
|
+
* ProgressBar tokens.
|
|
59
|
+
*/
|
|
60
|
+
tokens: getTokensPropType('ProgressBar'),
|
|
61
|
+
/**
|
|
62
|
+
* ProgressBar variant.
|
|
63
|
+
*/
|
|
64
|
+
variant: variantProp.propType
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default ProgressBar
|
package/src/Ribbon/Ribbon.jsx
CHANGED
|
@@ -22,6 +22,8 @@ const RibbonContainer = styled.div`
|
|
|
22
22
|
border-radius: ${({ borderRadius }) => borderRadius};
|
|
23
23
|
width: fit-content;
|
|
24
24
|
box-shadow: ${({ boxShadow, shouldWrap }) => shouldWrap && boxShadow};
|
|
25
|
+
border-bottom-right-radius: ${({ borderRadiusBottomLeft }) => borderRadiusBottomLeft};
|
|
26
|
+
border-bottom-left-radius: ${({ borderRadiusBottomRight }) => borderRadiusBottomRight};
|
|
25
27
|
`
|
|
26
28
|
|
|
27
29
|
const RibbonCurve = styled.div`
|
|
@@ -71,15 +73,18 @@ const Ribbon = ({
|
|
|
71
73
|
paddingLeft,
|
|
72
74
|
paddingRight,
|
|
73
75
|
paddingTop,
|
|
74
|
-
gradient
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const isOfferPurpose = purpose === 'offer'
|
|
80
|
-
const gradientBackground = `${gradientType}-gradient(${gradientAngle}deg, ${gradientColors[0].color}, ${gradientColors[1].color})`
|
|
81
|
-
const background = isOfferPurpose ? gradientBackground : backgroundColor
|
|
76
|
+
gradient,
|
|
77
|
+
fontColor,
|
|
78
|
+
borderRadiusBottomLeft,
|
|
79
|
+
borderRadiusBottomRight
|
|
80
|
+
} = useThemeTokens('Ribbon', tokens, { ...variant, wrap: shouldWrap })
|
|
82
81
|
|
|
82
|
+
let background = backgroundColor
|
|
83
|
+
if (gradient) {
|
|
84
|
+
const { type: gradientType, angle: gradientAngle, stops: gradientColors } = gradient
|
|
85
|
+
const gradientBackground = `${gradientType}-gradient(${gradientAngle}deg, ${gradientColors[0].color}, ${gradientColors[1].color})`
|
|
86
|
+
background = gradientBackground
|
|
87
|
+
}
|
|
83
88
|
return (
|
|
84
89
|
<RibbonWrapper left={left} top={top} data-testid="ribbon-wrapper" {...selectProps(rest)}>
|
|
85
90
|
<RibbonContainer
|
|
@@ -88,8 +93,15 @@ const Ribbon = ({
|
|
|
88
93
|
padding={`${paddingTop}px ${paddingRight}px ${paddingBottom}px ${paddingLeft}px`}
|
|
89
94
|
borderRadius={`${borderRadius}px`}
|
|
90
95
|
boxShadow={`${boxShadowPaddingTop}px ${boxShadowPaddingRight}px ${boxShadowPaddingBottom}px ${boxShadowPaddingLeft}px ${boxShadowColor}`}
|
|
96
|
+
borderRadiusBottomLeft={`${borderRadiusBottomLeft}px`}
|
|
97
|
+
borderRadiusBottomRight={`${borderRadiusBottomRight}px`}
|
|
91
98
|
>
|
|
92
|
-
<Typography
|
|
99
|
+
<Typography
|
|
100
|
+
variant={{ size: 'micro', bold: true, inverse: true }}
|
|
101
|
+
tokens={{ color: fontColor }}
|
|
102
|
+
>
|
|
103
|
+
{children}
|
|
104
|
+
</Typography>
|
|
93
105
|
</RibbonContainer>
|
|
94
106
|
{shouldWrap && (
|
|
95
107
|
<RibbonCurve
|
package/src/Spinner/Spinner.jsx
CHANGED
|
@@ -2,6 +2,7 @@ import React from 'react'
|
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
import styled from 'styled-components'
|
|
4
4
|
import { selectSystemProps, useScrollBlocking, useThemeTokens } from '@telus-uds/components-base'
|
|
5
|
+
import { Portal } from '@gorhom/portal'
|
|
5
6
|
import SpinnerContent from './SpinnerContent'
|
|
6
7
|
import { htmlAttrs, media } from '../utils'
|
|
7
8
|
import { BACKDROP_OPACITY, BACKDROP_Z_INDEX } from './constants'
|
|
@@ -86,23 +87,25 @@ const Spinner = ({
|
|
|
86
87
|
// Full screen spinner
|
|
87
88
|
if (fullScreen) {
|
|
88
89
|
return (
|
|
89
|
-
<
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
90
|
+
<Portal>
|
|
91
|
+
<FullscreenOverlay fullScreenOverLayBackground={fullScreenOverLayBackground}>
|
|
92
|
+
<SpinnerContainer
|
|
93
|
+
inline={inline}
|
|
94
|
+
fullScreen={fullScreen}
|
|
95
|
+
aria-live="assertive"
|
|
96
|
+
{...selectProps(rest)}
|
|
97
|
+
>
|
|
98
|
+
<SpinnerContent
|
|
99
|
+
label={label}
|
|
100
|
+
overlay={true}
|
|
101
|
+
size={size}
|
|
102
|
+
thickness={thickness}
|
|
103
|
+
sizeVariant={sizeVariant}
|
|
104
|
+
isStatic={isStatic}
|
|
105
|
+
/>
|
|
106
|
+
</SpinnerContainer>
|
|
107
|
+
</FullscreenOverlay>
|
|
108
|
+
</Portal>
|
|
106
109
|
)
|
|
107
110
|
}
|
|
108
111
|
|
package/src/Table/Cell.jsx
CHANGED
|
@@ -73,7 +73,10 @@ const Cell = ({ children, type = 'default', isFirstInRow, align = 'left' }) => {
|
|
|
73
73
|
cellPaddingTop,
|
|
74
74
|
cellPaddingRight,
|
|
75
75
|
cellPaddingLeft,
|
|
76
|
-
cellPaddingBottom
|
|
76
|
+
cellPaddingBottom,
|
|
77
|
+
fontName,
|
|
78
|
+
fontWeight,
|
|
79
|
+
fontSize
|
|
77
80
|
} = useThemeTokens('Table', tokens, variant)
|
|
78
81
|
const sharedProps = {
|
|
79
82
|
align,
|
|
@@ -86,6 +89,12 @@ const Cell = ({ children, type = 'default', isFirstInRow, align = 'left' }) => {
|
|
|
86
89
|
cellBoxShadowColor
|
|
87
90
|
}
|
|
88
91
|
|
|
92
|
+
const typographyTokens = {
|
|
93
|
+
fontName,
|
|
94
|
+
fontWeight,
|
|
95
|
+
fontSize
|
|
96
|
+
}
|
|
97
|
+
|
|
89
98
|
switch (type) {
|
|
90
99
|
case 'heading':
|
|
91
100
|
return (
|
|
@@ -95,7 +104,9 @@ const Cell = ({ children, type = 'default', isFirstInRow, align = 'left' }) => {
|
|
|
95
104
|
cellHeadingBoxShadowColor={cellHeadingBoxShadowColor}
|
|
96
105
|
{...sharedProps}
|
|
97
106
|
>
|
|
98
|
-
<Typography variant={{ size: 'h4' }}>
|
|
107
|
+
<Typography tokens={typographyTokens} variant={{ size: 'h4' }}>
|
|
108
|
+
{children}
|
|
109
|
+
</Typography>
|
|
99
110
|
</StyledHeading>
|
|
100
111
|
)
|
|
101
112
|
case 'subHeading':
|
|
@@ -105,7 +116,9 @@ const Cell = ({ children, type = 'default', isFirstInRow, align = 'left' }) => {
|
|
|
105
116
|
cellSubheadingBackground={cellSubheadingBackground}
|
|
106
117
|
{...sharedProps}
|
|
107
118
|
>
|
|
108
|
-
<Typography variant={{ size: 'h5' }}>
|
|
119
|
+
<Typography tokens={typographyTokens} variant={{ size: 'h5' }}>
|
|
120
|
+
{children}
|
|
121
|
+
</Typography>
|
|
109
122
|
</StyledSubHeading>
|
|
110
123
|
)
|
|
111
124
|
case 'rowHeading':
|
|
@@ -115,14 +128,18 @@ const Cell = ({ children, type = 'default', isFirstInRow, align = 'left' }) => {
|
|
|
115
128
|
cellRowHeadingBackground={cellRowHeadingBackground}
|
|
116
129
|
{...sharedProps}
|
|
117
130
|
>
|
|
118
|
-
<Typography variant={{ size: text === 'small' ? 'h5' : 'h4' }}>
|
|
131
|
+
<Typography tokens={typographyTokens} variant={{ size: text === 'small' ? 'h5' : 'h4' }}>
|
|
132
|
+
{children}
|
|
133
|
+
</Typography>
|
|
119
134
|
</StyledRowHeading>
|
|
120
135
|
)
|
|
121
136
|
|
|
122
137
|
default:
|
|
123
138
|
return (
|
|
124
139
|
<StyledCell cellBackground={cellBackground} {...sharedProps}>
|
|
125
|
-
<Typography variant={{ size: text }}>
|
|
140
|
+
<Typography tokens={{ fontSize }} variant={{ size: text }}>
|
|
141
|
+
{children}
|
|
142
|
+
</Typography>
|
|
126
143
|
</StyledCell>
|
|
127
144
|
)
|
|
128
145
|
}
|