@telus-uds/components-web 1.8.0 → 1.9.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.
Files changed (179) hide show
  1. package/CHANGELOG.md +27 -2
  2. package/lib/Autocomplete/Autocomplete.js +393 -0
  3. package/lib/Autocomplete/Loading.js +51 -0
  4. package/lib/Autocomplete/Suggestions.js +81 -0
  5. package/lib/Autocomplete/constants.js +19 -0
  6. package/lib/Autocomplete/dictionary.js +19 -0
  7. package/lib/Autocomplete/index.js +13 -0
  8. package/lib/Callout/Callout.js +3 -0
  9. package/lib/Card/Card.js +180 -0
  10. package/lib/Card/CardContent.js +110 -0
  11. package/lib/Card/CardFooter.js +98 -0
  12. package/lib/Card/index.js +13 -0
  13. package/lib/Countdown/Countdown.js +189 -0
  14. package/lib/Countdown/Segment.js +111 -0
  15. package/lib/Countdown/constants.js +14 -0
  16. package/lib/Countdown/dictionary.js +29 -0
  17. package/lib/Countdown/index.js +13 -0
  18. package/lib/Countdown/types.js +39 -0
  19. package/lib/Countdown/useCountdown.js +40 -0
  20. package/lib/Modal/ModalContent.js +11 -4
  21. package/lib/OptimizeImage/OptimizeImage.js +127 -0
  22. package/lib/OptimizeImage/index.js +13 -0
  23. package/lib/OptimizeImage/utils/getFallbackUrl.js +18 -0
  24. package/lib/OptimizeImage/utils/getOptimizedUrl.js +32 -0
  25. package/lib/OptimizeImage/utils/hasWebpSupport.js +38 -0
  26. package/lib/OptimizeImage/utils/index.js +31 -0
  27. package/lib/OptimizeImage/utils/isSvgUrl.js +10 -0
  28. package/lib/QuantitySelector/QuantitySelector.js +253 -0
  29. package/lib/QuantitySelector/dictionary.js +33 -0
  30. package/lib/QuantitySelector/index.js +13 -0
  31. package/lib/QuantitySelector/styles.js +40 -0
  32. package/lib/StoryCard/StoryCard.js +244 -0
  33. package/lib/StoryCard/index.js +13 -0
  34. package/lib/TermsAndConditions/ExpandCollapse.js +141 -0
  35. package/lib/TermsAndConditions/TermsAndConditions.js +221 -0
  36. package/lib/TermsAndConditions/dictionary.js +19 -0
  37. package/lib/TermsAndConditions/index.js +15 -0
  38. package/lib/Testimonial/Testimonial.js +226 -0
  39. package/lib/Testimonial/index.js +13 -0
  40. package/lib/Video/ControlBar/ControlBar.js +315 -0
  41. package/lib/Video/ControlBar/Controls/VideoButton/VideoButton.js +91 -0
  42. package/lib/Video/ControlBar/Controls/VideoMenu/VideoMenu.js +186 -0
  43. package/lib/Video/ControlBar/Controls/VideoProgressBar/VideoProgressBar.js +221 -0
  44. package/lib/Video/ControlBar/Controls/VolumeSlider/VolumeSlider.js +213 -0
  45. package/lib/Video/MiddleControlButton/MiddleControlButton.js +89 -0
  46. package/lib/Video/Video.js +1072 -0
  47. package/lib/Video/index.js +13 -0
  48. package/lib/Video/videoText.js +62 -0
  49. package/lib/WebVideo/WebVideo.js +170 -0
  50. package/lib/WebVideo/index.js +13 -0
  51. package/lib/baseExports.js +0 -6
  52. package/lib/index.js +91 -1
  53. package/lib/shared/VideoSplash/SplashButton/SplashButton.js +102 -0
  54. package/lib/shared/VideoSplash/SplashButtonWithDetails/SplashButtonWithDetails.js +234 -0
  55. package/lib/shared/VideoSplash/VideoSplash.js +86 -0
  56. package/lib/shared/VideoSplash/helpers.js +38 -0
  57. package/lib/utils/index.js +8 -0
  58. package/lib-module/Autocomplete/Autocomplete.js +369 -0
  59. package/lib-module/Autocomplete/Loading.js +38 -0
  60. package/lib-module/Autocomplete/Suggestions.js +64 -0
  61. package/lib-module/Autocomplete/constants.js +5 -0
  62. package/lib-module/Autocomplete/dictionary.js +12 -0
  63. package/lib-module/Autocomplete/index.js +2 -0
  64. package/lib-module/Callout/Callout.js +3 -0
  65. package/lib-module/Card/Card.js +158 -0
  66. package/lib-module/Card/CardContent.js +92 -0
  67. package/lib-module/Card/CardFooter.js +80 -0
  68. package/lib-module/Card/index.js +2 -0
  69. package/lib-module/Countdown/Countdown.js +165 -0
  70. package/lib-module/Countdown/Segment.js +94 -0
  71. package/lib-module/Countdown/constants.js +4 -0
  72. package/lib-module/Countdown/dictionary.js +22 -0
  73. package/lib-module/Countdown/index.js +2 -0
  74. package/lib-module/Countdown/types.js +23 -0
  75. package/lib-module/Countdown/useCountdown.js +32 -0
  76. package/lib-module/Modal/ModalContent.js +10 -4
  77. package/lib-module/OptimizeImage/OptimizeImage.js +106 -0
  78. package/lib-module/OptimizeImage/index.js +2 -0
  79. package/lib-module/OptimizeImage/utils/getFallbackUrl.js +8 -0
  80. package/lib-module/OptimizeImage/utils/getOptimizedUrl.js +22 -0
  81. package/lib-module/OptimizeImage/utils/hasWebpSupport.js +32 -0
  82. package/lib-module/OptimizeImage/utils/index.js +4 -0
  83. package/lib-module/OptimizeImage/utils/isSvgUrl.js +3 -0
  84. package/lib-module/QuantitySelector/QuantitySelector.js +232 -0
  85. package/lib-module/QuantitySelector/dictionary.js +26 -0
  86. package/lib-module/QuantitySelector/index.js +2 -0
  87. package/lib-module/QuantitySelector/styles.js +21 -0
  88. package/lib-module/StoryCard/StoryCard.js +220 -0
  89. package/lib-module/StoryCard/index.js +2 -0
  90. package/lib-module/TermsAndConditions/ExpandCollapse.js +120 -0
  91. package/lib-module/TermsAndConditions/TermsAndConditions.js +193 -0
  92. package/lib-module/TermsAndConditions/dictionary.js +12 -0
  93. package/lib-module/TermsAndConditions/index.js +1 -0
  94. package/lib-module/Testimonial/Testimonial.js +204 -0
  95. package/lib-module/Testimonial/index.js +2 -0
  96. package/lib-module/Video/ControlBar/ControlBar.js +292 -0
  97. package/lib-module/Video/ControlBar/Controls/VideoButton/VideoButton.js +74 -0
  98. package/lib-module/Video/ControlBar/Controls/VideoMenu/VideoMenu.js +167 -0
  99. package/lib-module/Video/ControlBar/Controls/VideoProgressBar/VideoProgressBar.js +201 -0
  100. package/lib-module/Video/ControlBar/Controls/VolumeSlider/VolumeSlider.js +193 -0
  101. package/lib-module/Video/MiddleControlButton/MiddleControlButton.js +72 -0
  102. package/lib-module/Video/Video.js +1042 -0
  103. package/lib-module/Video/index.js +2 -0
  104. package/lib-module/Video/videoText.js +55 -0
  105. package/lib-module/WebVideo/WebVideo.js +144 -0
  106. package/lib-module/WebVideo/index.js +2 -0
  107. package/lib-module/baseExports.js +1 -1
  108. package/lib-module/index.js +10 -0
  109. package/lib-module/shared/VideoSplash/SplashButton/SplashButton.js +85 -0
  110. package/lib-module/shared/VideoSplash/SplashButtonWithDetails/SplashButtonWithDetails.js +216 -0
  111. package/lib-module/shared/VideoSplash/VideoSplash.js +65 -0
  112. package/lib-module/shared/VideoSplash/helpers.js +23 -0
  113. package/lib-module/utils/index.js +2 -1
  114. package/package.json +7 -5
  115. package/src/Autocomplete/Autocomplete.jsx +354 -0
  116. package/src/Autocomplete/Loading.jsx +18 -0
  117. package/src/Autocomplete/Suggestions.jsx +52 -0
  118. package/src/Autocomplete/constants.js +6 -0
  119. package/src/Autocomplete/dictionary.js +12 -0
  120. package/src/Autocomplete/index.js +3 -0
  121. package/src/Callout/Callout.jsx +1 -1
  122. package/src/Card/Card.jsx +170 -0
  123. package/src/Card/CardContent.jsx +88 -0
  124. package/src/Card/CardFooter.jsx +70 -0
  125. package/src/Card/index.js +3 -0
  126. package/src/Countdown/Countdown.jsx +144 -0
  127. package/src/Countdown/Segment.jsx +69 -0
  128. package/src/Countdown/constants.js +4 -0
  129. package/src/Countdown/dictionary.js +22 -0
  130. package/src/Countdown/index.js +3 -0
  131. package/src/Countdown/types.js +23 -0
  132. package/src/Countdown/useCountdown.js +34 -0
  133. package/src/Modal/ModalContent.jsx +8 -4
  134. package/src/OptimizeImage/OptimizeImage.jsx +131 -0
  135. package/src/OptimizeImage/index.js +3 -0
  136. package/src/OptimizeImage/utils/getFallbackUrl.js +9 -0
  137. package/src/OptimizeImage/utils/getOptimizedUrl.js +30 -0
  138. package/src/OptimizeImage/utils/hasWebpSupport.js +33 -0
  139. package/src/OptimizeImage/utils/index.js +5 -0
  140. package/src/OptimizeImage/utils/isSvgUrl.js +3 -0
  141. package/src/QuantitySelector/QuantitySelector.jsx +245 -0
  142. package/src/QuantitySelector/dictionary.js +27 -0
  143. package/src/QuantitySelector/index.js +3 -0
  144. package/src/QuantitySelector/styles.js +83 -0
  145. package/src/StoryCard/StoryCard.jsx +198 -0
  146. package/src/StoryCard/index.js +3 -0
  147. package/src/TermsAndConditions/ExpandCollapse.jsx +106 -0
  148. package/src/TermsAndConditions/TermsAndConditions.jsx +161 -0
  149. package/src/TermsAndConditions/dictionary.js +12 -0
  150. package/src/TermsAndConditions/index.js +1 -0
  151. package/src/Testimonial/Testimonial.jsx +169 -0
  152. package/src/Testimonial/index.js +3 -0
  153. package/src/Video/ControlBar/ControlBar.jsx +261 -0
  154. package/src/Video/ControlBar/Controls/VideoButton/VideoButton.jsx +61 -0
  155. package/src/Video/ControlBar/Controls/VideoMenu/VideoMenu.jsx +159 -0
  156. package/src/Video/ControlBar/Controls/VideoProgressBar/VideoProgressBar.jsx +185 -0
  157. package/src/Video/ControlBar/Controls/VolumeSlider/VolumeSlider.jsx +184 -0
  158. package/src/Video/MiddleControlButton/MiddleControlButton.jsx +64 -0
  159. package/src/Video/Video.jsx +988 -0
  160. package/src/Video/index.js +3 -0
  161. package/src/Video/videoText.js +58 -0
  162. package/src/WebVideo/WebVideo.jsx +131 -0
  163. package/src/WebVideo/index.js +3 -0
  164. package/src/baseExports.js +0 -1
  165. package/src/index.js +10 -0
  166. package/src/shared/VideoSplash/SplashButton/SplashButton.jsx +64 -0
  167. package/src/shared/VideoSplash/SplashButtonWithDetails/SplashButtonWithDetails.jsx +128 -0
  168. package/src/shared/VideoSplash/VideoSplash.jsx +50 -0
  169. package/src/shared/VideoSplash/helpers.js +27 -0
  170. package/src/utils/index.js +10 -1
  171. package/types/Autocomplete.d.ts +32 -0
  172. package/types/Card.d.ts +45 -0
  173. package/types/ControlBar.d.ts +59 -0
  174. package/types/MiddleControlButton.d.ts +15 -0
  175. package/types/Video.d.ts +39 -0
  176. package/types/VideoButton.d.ts +14 -0
  177. package/types/VideoMenu.d.ts +16 -0
  178. package/types/VideoProgressBar.d.ts +17 -0
  179. package/types/VolumeSlider.d.ts +20 -0
@@ -0,0 +1,106 @@
1
+ import React, { forwardRef } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import {
4
+ ExpandCollapse as ExpandCollapseBase,
5
+ Icon,
6
+ useThemeTokensCallback
7
+ } from '@telus-uds/components-base'
8
+ import styled from 'styled-components'
9
+
10
+ const ExpandCollapseControl = styled.div({
11
+ alignItems: 'center',
12
+ cursor: 'pointer',
13
+ display: 'flex',
14
+ flex: 1,
15
+ justifyContent: 'flex-start'
16
+ })
17
+
18
+ const ExpandCollapseIconContainer = styled.div(({ tokens }) => ({
19
+ alignItems: tokens.expandIconContainerAlignItems,
20
+ border: `${tokens.expandIconContainerBorder}px solid ${tokens.expandIconContainerBorderColor}`,
21
+ borderRadius: '50%',
22
+ display: 'flex',
23
+ height: tokens.expandIconContainerHeight,
24
+ justifyContent: tokens.expandIconContainerJustifyContent,
25
+ margin: `${tokens.expandIconContainerMarginX}px ${tokens.expandIconContainerMarginY}px`,
26
+ width: tokens.expandIconContainerWidth
27
+ }))
28
+
29
+ const ExpandCollapseTitle = styled.h4(({ tokens }) => ({
30
+ color: tokens.expandTitleColor,
31
+ fontFamily: `${tokens.listFontName}${tokens.listFontWeight}normal`,
32
+ fontSize: tokens.expandTitleFontSize,
33
+ lineHeight: tokens.expandTitleLineHeight,
34
+ margin: `${tokens.expandTitleMarginX}px ${tokens.expandTitleMarginY}px`
35
+ }))
36
+
37
+ const ExpandCollapse = forwardRef(
38
+ ({ children, collapseTitle, expandTitle = collapseTitle }, ref) => {
39
+ const getTokens = useThemeTokensCallback('TermsAndConditions', {}, {})
40
+ const {
41
+ expandBaseBorderWidth,
42
+ expandContentPaddingBottom,
43
+ expandContentPaddingLeft,
44
+ expandContentPaddingRight,
45
+ expandContentPaddingTop
46
+ } = getTokens()
47
+
48
+ return (
49
+ <ExpandCollapseBase
50
+ tokens={{
51
+ borderWidth: expandBaseBorderWidth
52
+ }}
53
+ >
54
+ {(expandProps) => (
55
+ <ExpandCollapseBase.Panel
56
+ {...expandProps}
57
+ panelId="ExpandCollapsePanel"
58
+ controlTokens={{ icon: null }}
59
+ // TODO refactor
60
+ // eslint-disable-next-line react/no-unstable-nested-components
61
+ control={(pressableState) => {
62
+ const { expanded } = pressableState || {}
63
+ const { icon } = getTokens({ expanded })
64
+
65
+ return (
66
+ <ExpandCollapseControl
67
+ onClick={(event) => expandProps.onToggle('ExpandCollapsePanel', event)}
68
+ ref={ref}
69
+ >
70
+ <ExpandCollapseIconContainer tokens={getTokens()}>
71
+ <Icon icon={icon} variant={{ size: 'small' }} />
72
+ </ExpandCollapseIconContainer>
73
+ <ExpandCollapseTitle tokens={getTokens()}>
74
+ {expanded ? expandTitle : collapseTitle}
75
+ </ExpandCollapseTitle>
76
+ </ExpandCollapseControl>
77
+ )
78
+ }}
79
+ tokens={{
80
+ contentPaddingBottom: expandContentPaddingBottom,
81
+ contentPaddingLeft: expandContentPaddingLeft,
82
+ contentPaddingRight: expandContentPaddingRight,
83
+ contentPaddingTop: expandContentPaddingTop
84
+ }}
85
+ >
86
+ {children}
87
+ </ExpandCollapseBase.Panel>
88
+ )}
89
+ </ExpandCollapseBase>
90
+ )
91
+ }
92
+ )
93
+
94
+ ExpandCollapse.displayName = 'ExpandCollapse'
95
+
96
+ ExpandCollapse.propTypes = {
97
+ children: PropTypes.node.isRequired,
98
+ collapseTitle: PropTypes.string.isRequired,
99
+ expandTitle: PropTypes.string
100
+ }
101
+
102
+ ExpandCollapse.defaultProps = {
103
+ expandTitle: undefined
104
+ }
105
+
106
+ export default ExpandCollapse
@@ -0,0 +1,161 @@
1
+ import React, { forwardRef } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import styled from 'styled-components'
4
+ import {
5
+ Box,
6
+ Divider,
7
+ selectSystemProps,
8
+ Typography,
9
+ useCopy,
10
+ useThemeTokens
11
+ } from '@telus-uds/components-base'
12
+ import ExpandCollapse from './ExpandCollapse'
13
+ import OrderedListBase from '../OrderedList/OrderedListBase'
14
+ import { htmlAttrs, media, renderStructuredContent } from '../utils'
15
+ import dictionary from './dictionary'
16
+
17
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs])
18
+
19
+ const ContentContainer = styled.div(({ tokens }) => ({
20
+ paddingBottom: tokens.contentPaddingBottom,
21
+ paddingLeft: tokens.contentPaddingLeft,
22
+ ...media().from('md').css({
23
+ paddingBottom: tokens.mdContentPaddingBottom,
24
+ paddingLeft: tokens.mdContentPaddingLeft
25
+ })
26
+ }))
27
+
28
+ const Ordered = styled(OrderedListBase)(({ tokens }) => ({
29
+ listStylePosition: 'outside',
30
+ padding: tokens.orderedPadding
31
+ }))
32
+
33
+ const Unordered = styled.ul(({ tokens }) => ({
34
+ listStyleType: 'none',
35
+ margin: 0,
36
+ padding: tokens.unorderedPadding
37
+ }))
38
+
39
+ const ListItem = styled(OrderedListBase.Item)(({ tokens }) => ({
40
+ display: 'list-item',
41
+ '&::marker': {
42
+ fontFamily: `${tokens.listFontName}${tokens.listFontWeight}normal`,
43
+ fontSize: tokens.listFontSize,
44
+ lineHeight: tokens.listLineHeight,
45
+ textAlign: 'end !important'
46
+ },
47
+ color: tokens.listColor,
48
+ fontFamily: `${tokens.listFontName}${tokens.listFontWeight}normal`,
49
+ fontSize: tokens.listFontSize,
50
+ lineHeight: tokens.listLineHeight,
51
+ marginBottom: tokens.listMarginBottom,
52
+ marginLeft: tokens.listMarginLeft,
53
+ wordBreak: 'break-word'
54
+ }))
55
+
56
+ const NonIndexedContentTitle = styled.div(({ tokens }) => ({
57
+ color: tokens.titleColor,
58
+ fontSize: tokens.titleFontSize,
59
+ lineHeight: tokens.titleLineHeight,
60
+ paddingLeft: tokens.titlePaddingLeft
61
+ }))
62
+
63
+ /**
64
+ * Use `TermsAndConditions` to display important legal content.
65
+ *
66
+ * ## Usage Criteria
67
+ *
68
+ * - Display before the page footer, except for call-to-action callback cards
69
+ * - Responsive display based on breakpoints
70
+ * - Use `copy` to set language, ‘en’ for English or ‘fr’ for French
71
+ */
72
+ const TermsAndConditions = forwardRef(
73
+ ({ copy = 'en', indexedContent, nonIndexedContent, tokens, variant = {}, ...rest }, ref) => {
74
+ const getCopy = useCopy({ dictionary, copy })
75
+ const hasIndexedContent = indexedContent.length > 0
76
+ const hasNonIndexedContent = nonIndexedContent.length > 0
77
+
78
+ const themeTokens = useThemeTokens('TermsAndConditions', tokens, variant)
79
+
80
+ return (
81
+ <div {...selectProps(rest)}>
82
+ <Divider />
83
+ <ExpandCollapse
84
+ collapseTitle={getCopy('headingView')}
85
+ expandTitle={getCopy('headingHide')}
86
+ ref={ref}
87
+ >
88
+ <ContentContainer tokens={themeTokens}>
89
+ {hasIndexedContent && (
90
+ <Ordered tokens={themeTokens}>
91
+ {indexedContent.map((contentItem, idx) => (
92
+ // eslint-disable-next-line react/no-array-index-key
93
+ <ListItem tokens={themeTokens} key={idx}>
94
+ {renderStructuredContent(contentItem)}
95
+ </ListItem>
96
+ ))}
97
+ </Ordered>
98
+ )}
99
+ {hasNonIndexedContent && (
100
+ <Box between={3}>
101
+ <NonIndexedContentTitle tokens={themeTokens}>
102
+ <Typography block heading variant={{ size: 'h4' }}>
103
+ {getCopy('nonIndexedTitle')}
104
+ </Typography>
105
+ </NonIndexedContentTitle>
106
+ <Unordered tokens={themeTokens}>
107
+ {nonIndexedContent.map((contentItem, idx) => (
108
+ // eslint-disable-next-line react/no-array-index-key
109
+ <ListItem tokens={themeTokens} key={idx}>
110
+ {renderStructuredContent(contentItem)}
111
+ </ListItem>
112
+ ))}
113
+ </Unordered>
114
+ </Box>
115
+ )}
116
+ </ContentContainer>
117
+ </ExpandCollapse>
118
+ <Divider />
119
+ </div>
120
+ )
121
+ }
122
+ )
123
+
124
+ TermsAndConditions.displayName = 'TermsAndConditions'
125
+
126
+ TermsAndConditions.propTypes = {
127
+ ...selectedSystemPropTypes,
128
+ /**
129
+ * Use the `copy` prop to either select provided English or French copy by passing 'en' or 'fr' respectively.
130
+ *
131
+ * To provide your own, pass a JSON object with the keys `headingView`, `headingHide`, and `nonIndexedTitle`.
132
+ */
133
+ copy: PropTypes.oneOfType([
134
+ PropTypes.oneOf(['en', 'fr']),
135
+ PropTypes.shape({
136
+ headingView: PropTypes.string,
137
+ headingHide: PropTypes.string,
138
+ nonIndexedTitle: PropTypes.string
139
+ })
140
+ ]),
141
+ /**
142
+ * An array of nodes, strings, or a combination to be displayed in an ordered list.
143
+ *
144
+ * Each item in the array must have a corresponding superscript in the page.
145
+ */
146
+ indexedContent: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.node, PropTypes.string])),
147
+ /**
148
+ * An array of nodes, strings, or a combination to be displayed in an unordered list.
149
+ *
150
+ * nonIndexedContent do not have a corresponding superscript and instead apply to the page as a whole.
151
+ */
152
+ nonIndexedContent: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.node, PropTypes.string]))
153
+ }
154
+
155
+ TermsAndConditions.defaultProps = {
156
+ copy: 'en',
157
+ indexedContent: [],
158
+ nonIndexedContent: []
159
+ }
160
+
161
+ export default TermsAndConditions
@@ -0,0 +1,12 @@
1
+ export default {
2
+ en: {
3
+ headingHide: 'Hide terms and conditions',
4
+ headingView: 'View terms and conditions',
5
+ nonIndexedTitle: 'The following applies to all terms and conditions above'
6
+ },
7
+ fr: {
8
+ headingHide: 'Masquer les modalités et conditions',
9
+ headingView: 'Voir les modalités et conditions',
10
+ nonIndexedTitle: 'Ce qui suit s’applique aux modalités et conditions ci-dessus'
11
+ }
12
+ }
@@ -0,0 +1 @@
1
+ export { default } from './TermsAndConditions'
@@ -0,0 +1,169 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import styled from 'styled-components'
4
+ import { Icon, selectSystemProps, Typography, useThemeTokens } from '@telus-uds/components-base'
5
+ import Image from '../Image'
6
+ import { htmlAttrs } from '../utils'
7
+
8
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs])
9
+
10
+ const TestimonialContainer = styled.figure(({ testimonialContainerGap }) => ({
11
+ display: 'flex',
12
+ flexDirection: 'column',
13
+ gap: testimonialContainerGap,
14
+ margin: 0
15
+ }))
16
+
17
+ const QuoteContainer = styled.div(({ quoteContainerGap }) => ({
18
+ display: 'flex',
19
+ alignItems: 'center',
20
+ gap: quoteContainerGap
21
+ }))
22
+
23
+ const Divider = styled.div(({ dividerBorder, dividerBackgroundColor }) => ({
24
+ height: dividerBorder,
25
+ background: dividerBackgroundColor,
26
+ width: '100%'
27
+ }))
28
+
29
+ const BlockQuote = styled.blockquote({
30
+ margin: 0
31
+ })
32
+
33
+ const AuthorInfoContainer = styled.div({
34
+ display: 'flex',
35
+ flexDirection: 'column'
36
+ })
37
+
38
+ const Figcaption = styled.figcaption(({ figcaptionGap }) => ({
39
+ display: 'flex',
40
+ alignItems: 'center',
41
+ gap: figcaptionGap
42
+ }))
43
+
44
+ const Testimonial = ({
45
+ showDivider,
46
+ testimonial,
47
+ title,
48
+ imageSrc,
49
+ image = imageSrc,
50
+ additionalInfo,
51
+ testimonialStyle = 'heading',
52
+ tokens,
53
+ variant = {},
54
+ ...rest
55
+ }) => {
56
+ const {
57
+ testimonialContainerGap,
58
+ quoteContainerGap,
59
+ dividerBorder,
60
+ dividerBackgroundColor,
61
+ figcaptionGap,
62
+ textColor,
63
+ icon,
64
+ iconColor,
65
+ imageSize
66
+ } = useThemeTokens('Testimonial', tokens, variant)
67
+ return (
68
+ <TestimonialContainer testimonialContainerGap={testimonialContainerGap} {...selectProps(rest)}>
69
+ <QuoteContainer quoteContainerGap={quoteContainerGap}>
70
+ <Icon tokens={{ color: iconColor }} variant={{ size: 'micro' }} icon={icon} />
71
+ {showDivider && (
72
+ <Divider
73
+ dividerBackgroundColor={dividerBackgroundColor}
74
+ dividerBorder={dividerBorder}
75
+ role="separator"
76
+ />
77
+ )}
78
+ </QuoteContainer>
79
+ <BlockQuote>
80
+ <Typography
81
+ variant={{ size: testimonialStyle === 'large' ? 'large' : 'h3' }}
82
+ tokens={{
83
+ color: textColor,
84
+ fontWeight: '400'
85
+ }}
86
+ >
87
+ {`\u201C${testimonial}\u201D`}
88
+ </Typography>
89
+ </BlockQuote>
90
+ {(image || title || additionalInfo) && (
91
+ <Figcaption figcaptionGap={figcaptionGap}>
92
+ {image &&
93
+ (typeof image === 'string' ? (
94
+ <Image
95
+ rounded="circle"
96
+ src={image}
97
+ alt={title}
98
+ width={imageSize}
99
+ height={imageSize}
100
+ />
101
+ ) : (
102
+ image
103
+ ))}
104
+ {(title || additionalInfo) && (
105
+ <AuthorInfoContainer>
106
+ {title && (
107
+ <Typography
108
+ variant={{ size: 'small', colour: 'secondary' }}
109
+ tokens={{ fontWeight: '500' }}
110
+ >
111
+ {title}
112
+ </Typography>
113
+ )}
114
+ {additionalInfo && (
115
+ <Typography
116
+ variant={{ size: 'micro', colour: 'secondary' }}
117
+ tokens={{ fontWeight: '400' }}
118
+ >
119
+ {additionalInfo}
120
+ </Typography>
121
+ )}
122
+ </AuthorInfoContainer>
123
+ )}
124
+ </Figcaption>
125
+ )}
126
+ {showDivider && (
127
+ <Divider
128
+ dividerBackgroundColor={dividerBackgroundColor}
129
+ dividerBorder={dividerBorder}
130
+ role="separator"
131
+ />
132
+ )}
133
+ </TestimonialContainer>
134
+ )
135
+ }
136
+
137
+ Testimonial.propTypes = {
138
+ ...selectedSystemPropTypes,
139
+ /**
140
+ * Testimonial content
141
+ */
142
+ testimonial: PropTypes.string.isRequired,
143
+ /**
144
+ * Testimonial author or source
145
+ */
146
+ title: PropTypes.string,
147
+ /**
148
+ * Additional information on the author or source
149
+ */
150
+ additionalInfo: PropTypes.string,
151
+ /**
152
+ * Testimonial style
153
+ */
154
+ testimonialStyle: PropTypes.oneOf(['large', 'heading']),
155
+ /**
156
+ * Whether to show or not dividers at the top and the bottom of the testimonial
157
+ showDivider: PropTypes.bool,
158
+ /**
159
+ * The src attribute for the Image component or custom JSX content to render instead
160
+ */
161
+ image: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
162
+ /**
163
+ * The src attribute for the `Image` component to be displayed on the testimonial
164
+ * @deprecated please use the `image` prop instead
165
+ */
166
+ imageSrc: PropTypes.string
167
+ }
168
+
169
+ export default Testimonial
@@ -0,0 +1,3 @@
1
+ import Testimonial from './Testimonial'
2
+
3
+ export default Testimonial