@telus-uds/components-base 0.0.2-prerelease.1 → 0.0.2-prerelease.5
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 +36 -0
- package/__fixtures__/testTheme.js +264 -84
- package/__tests__/Box/Box.test.jsx +81 -58
- package/__tests__/Card/Card.test.jsx +63 -0
- package/__tests__/Divider/Divider.test.jsx +26 -5
- package/__tests__/Feedback/Feedback.test.jsx +42 -0
- package/__tests__/FlexGrid/Col.test.jsx +5 -0
- package/__tests__/Pagination/Pagination.test.jsx +160 -0
- package/__tests__/Spacer/Spacer.test.jsx +63 -0
- package/__tests__/StackView/StackView.test.jsx +242 -0
- package/__tests__/StackView/StackWrap.test.jsx +47 -0
- package/__tests__/StackView/getStackedContent.test.jsx +295 -0
- package/__tests__/TextInput/TextInput.test.jsx +146 -0
- package/__tests__/ThemeProvider/useThemeTokens.test.jsx +5 -3
- package/__tests__/utils/spacing.test.jsx +273 -0
- package/__tests__/utils/useUniqueId.test.js +31 -0
- package/babel.config.json +8 -0
- package/jest.config.js +7 -6
- package/lib/A11yInfoProvider/index.js +2 -2
- package/lib/A11yText/index.js +1 -3
- package/lib/ActivityIndicator/Spinner.web.js +3 -5
- package/lib/Box/Box.js +117 -82
- package/lib/Button/Button.js +1 -3
- package/lib/Button/ButtonBase.js +9 -21
- package/lib/Button/ButtonGroup.js +14 -25
- package/lib/Button/ButtonLink.js +1 -3
- package/lib/Card/Card.js +103 -0
- package/lib/Card/index.js +2 -0
- package/lib/Divider/Divider.js +40 -4
- package/lib/ExpandCollapse/Accordion.js +1 -3
- package/lib/ExpandCollapse/Control.js +3 -5
- package/lib/ExpandCollapse/Panel.js +2 -4
- package/lib/Feedback/Feedback.js +110 -0
- package/lib/Feedback/index.js +2 -0
- package/lib/FlexGrid/Col/Col.js +3 -5
- package/lib/FlexGrid/FlexGrid.js +1 -3
- package/lib/FlexGrid/Row/Row.js +1 -3
- package/lib/FlexGrid/providers/GutterContext.js +1 -1
- package/lib/Icon/Icon.js +1 -1
- package/lib/InputLabel/InputLabel.js +86 -0
- package/lib/InputLabel/LabelContent.native.js +8 -0
- package/lib/InputLabel/LabelContent.web.js +17 -0
- package/lib/InputLabel/index.js +2 -0
- package/lib/Link/ChevronLink.js +1 -3
- package/lib/Link/Link.js +1 -3
- package/lib/Link/LinkBase.js +11 -7
- package/lib/Link/TextButton.js +1 -3
- package/lib/Pagination/PageButton.js +85 -0
- package/lib/Pagination/Pagination.js +118 -0
- package/lib/Pagination/SideButton.js +108 -0
- package/lib/Pagination/dictionary.js +18 -0
- package/lib/Pagination/index.js +2 -0
- package/lib/Pagination/useCopy.js +10 -0
- package/lib/Pagination/usePagination.js +70 -0
- package/lib/SideNav/Item.js +4 -6
- package/lib/SideNav/ItemsGroup.js +11 -11
- package/lib/SideNav/SideNav.js +2 -4
- package/lib/Spacer/Spacer.js +98 -0
- package/lib/Spacer/index.js +2 -0
- package/lib/StackView/StackView.js +105 -0
- package/lib/StackView/StackWrap.js +32 -0
- package/lib/StackView/StackWrap.native.js +3 -0
- package/lib/StackView/StackWrapBox.js +85 -0
- package/lib/StackView/StackWrapGap.js +45 -0
- package/lib/StackView/common.js +30 -0
- package/lib/StackView/getStackedContent.js +111 -0
- package/lib/StackView/index.js +5 -0
- package/lib/TextInput/TextInput.js +337 -0
- package/lib/TextInput/index.js +2 -0
- package/lib/ThemeProvider/ThemeProvider.js +2 -2
- package/lib/ThemeProvider/useThemeTokens.js +34 -6
- package/lib/ThemeProvider/utils/theme-tokens.js +37 -9
- package/lib/ToggleSwitch/ToggleSwitch.js +17 -47
- package/lib/Typography/Typography.js +1 -7
- package/lib/ViewportProvider/index.js +1 -1
- package/lib/index.js +8 -1
- package/lib/utils/index.js +2 -1
- package/lib/utils/input.js +3 -1
- package/lib/utils/propTypes.js +103 -8
- package/lib/utils/spacing/index.js +2 -0
- package/lib/utils/spacing/useSpacingScale.js +102 -0
- package/lib/utils/spacing/utils.js +32 -0
- package/lib/utils/useUniqueId.js +12 -0
- package/package.json +6 -9
- package/release-context.json +4 -4
- package/src/Box/Box.jsx +117 -80
- package/src/Button/ButtonBase.jsx +8 -21
- package/src/Button/ButtonGroup.jsx +13 -17
- package/src/Card/Card.jsx +101 -0
- package/src/Card/index.js +3 -0
- package/src/Divider/Divider.jsx +38 -3
- package/src/ExpandCollapse/Control.jsx +2 -3
- package/src/Feedback/Feedback.jsx +99 -0
- package/src/Feedback/index.js +3 -0
- package/src/FlexGrid/Col/Col.jsx +4 -2
- package/src/Icon/Icon.jsx +2 -1
- package/src/InputLabel/InputLabel.jsx +99 -0
- package/src/InputLabel/LabelContent.native.jsx +6 -0
- package/src/InputLabel/LabelContent.web.jsx +13 -0
- package/src/InputLabel/index.js +3 -0
- package/src/Link/LinkBase.jsx +9 -3
- package/src/Pagination/PageButton.jsx +80 -0
- package/src/Pagination/Pagination.jsx +135 -0
- package/src/Pagination/SideButton.jsx +93 -0
- package/src/Pagination/dictionary.js +18 -0
- package/src/Pagination/index.js +3 -0
- package/src/Pagination/useCopy.js +7 -0
- package/src/Pagination/usePagination.js +69 -0
- package/src/SideNav/Item.jsx +3 -3
- package/src/SideNav/ItemsGroup.jsx +11 -13
- package/src/Spacer/Spacer.jsx +91 -0
- package/src/Spacer/index.js +3 -0
- package/src/StackView/StackView.jsx +103 -0
- package/src/StackView/StackWrap.jsx +33 -0
- package/src/StackView/StackWrap.native.jsx +4 -0
- package/src/StackView/StackWrapBox.jsx +82 -0
- package/src/StackView/StackWrapGap.jsx +39 -0
- package/src/StackView/common.jsx +28 -0
- package/src/StackView/getStackedContent.jsx +106 -0
- package/src/StackView/index.js +6 -0
- package/src/TextInput/TextInput.jsx +325 -0
- package/src/TextInput/index.js +3 -0
- package/src/ThemeProvider/useThemeTokens.js +34 -7
- package/src/ThemeProvider/utils/theme-tokens.js +37 -8
- package/src/ToggleSwitch/ToggleSwitch.jsx +23 -43
- package/src/Typography/Typography.jsx +0 -4
- package/src/index.js +8 -1
- package/src/utils/index.js +1 -0
- package/src/utils/input.js +2 -1
- package/src/utils/propTypes.js +105 -16
- package/src/utils/spacing/index.js +3 -0
- package/src/utils/spacing/useSpacingScale.js +93 -0
- package/src/utils/spacing/utils.js +28 -0
- package/src/utils/useUniqueId.js +14 -0
- package/stories/A11yText/A11yText.stories.jsx +11 -5
- package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +11 -2
- package/stories/Box/Box.stories.jsx +46 -17
- package/stories/Button/Button.stories.jsx +17 -21
- package/stories/Button/ButtonGroup.stories.jsx +2 -1
- package/stories/Button/ButtonLink.stories.jsx +6 -4
- package/stories/Card/Card.stories.jsx +62 -0
- package/stories/Divider/Divider.stories.jsx +26 -2
- package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +74 -79
- package/stories/Feedback/Feedback.stories.jsx +97 -0
- package/stories/FlexGrid/01 FlexGrid.stories.jsx +20 -7
- package/stories/Icon/Icon.stories.jsx +11 -3
- package/stories/InputLabel/InputLabel.stories.jsx +37 -0
- package/stories/Link/ChevronLink.stories.jsx +20 -4
- package/stories/Link/Link.stories.jsx +24 -3
- package/stories/Link/TextButton.stories.jsx +24 -3
- package/stories/Pagination/Pagination.stories.jsx +64 -0
- package/stories/SideNav/SideNav.stories.jsx +17 -2
- package/stories/Spacer/Spacer.stories.jsx +33 -0
- package/stories/StackView/StackView.stories.jsx +65 -0
- package/stories/StackView/StackWrap.stories.jsx +52 -0
- package/stories/TextInput/TextInput.stories.jsx +103 -0
- package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +16 -3
- package/stories/Typography/Typography.stories.jsx +12 -3
- package/stories/platform-supports.web.jsx +1 -1
- package/stories/supports.jsx +113 -13
- package/babel.config.js +0 -3
package/lib/utils/propTypes.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import PropTypes from 'prop-types';
|
|
2
2
|
import { Linking, Platform } from 'react-native';
|
|
3
3
|
import { tokenKeys } from '@telus-uds/tools-theme';
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {{[key: string]: string|number|boolean}} AppearanceSet
|
|
6
|
+
* @typedef {AppearanceSet} VariantProp
|
|
7
|
+
*/
|
|
8
|
+
|
|
4
9
|
export const variantProp = {
|
|
5
10
|
/**
|
|
6
11
|
* 'variant' is an optional object prop on all themable components.
|
|
@@ -12,8 +17,11 @@ export const variantProp = {
|
|
|
12
17
|
* the exact shape of variant props is not enforced using PropTypes.
|
|
13
18
|
*/
|
|
14
19
|
propType: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]))
|
|
15
|
-
};
|
|
16
|
-
|
|
20
|
+
}; // Tokens can be primitive values (e.g. `'rgba(0,0,0,0'`, `12`), or objects of such values
|
|
21
|
+
// such as tokens that describe shadow (e.g. shadow: { inset: true, color: 'rgba(...)' ... })
|
|
22
|
+
|
|
23
|
+
const tokenValue = PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]);
|
|
24
|
+
const tokenValueType = PropTypes.oneOfType([tokenValue, PropTypes.objectOf(tokenValue)]);
|
|
17
25
|
|
|
18
26
|
const getTokenNames = componentName => {
|
|
19
27
|
const componentTokenNames = tokenKeys[componentName];
|
|
@@ -36,15 +44,19 @@ export const selectTokens = (componentName, tokens) => {
|
|
|
36
44
|
} : validTokens, {});
|
|
37
45
|
return filteredTokens;
|
|
38
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* @typedef {string|number|boolean|{[key: string]:string|number|boolean}} TokenValue
|
|
49
|
+
* @typedef {{[key: string]: TokenValue}} TokensSet
|
|
50
|
+
* @typedef {(AppearanceSet) => TokensSet} TokensGetter
|
|
51
|
+
* @typedef {TokensSet|TokensGetter} TokensProp
|
|
52
|
+
*/
|
|
53
|
+
|
|
39
54
|
/**
|
|
40
55
|
* 'tokens' is an optional object prop on all themable components. Its keys must match the
|
|
41
56
|
* token keys in the component's theme schema.
|
|
42
57
|
*
|
|
43
58
|
* This prop is intended to be used as an 'escape hatch' for difficult or exceptional cases
|
|
44
59
|
* where the main theming system doesn't apply. It is intentionally permissive about values.
|
|
45
|
-
*
|
|
46
|
-
* Be careful about passing a token key with value `undefined`, as this will override any tokens from
|
|
47
|
-
* the theme. If a key is set on a `tokens` prop object, tokens from the theme will be overridden.
|
|
48
60
|
*/
|
|
49
61
|
|
|
50
62
|
export const getTokensPropType = componentName => PropTypes.oneOfType([PropTypes.shape(Object.fromEntries(getTokenNames(componentName).map(key => [key, tokenValueType]))), PropTypes.func // function that takes current appearances and returns above shape
|
|
@@ -247,14 +259,88 @@ export const linkProps = {
|
|
|
247
259
|
return Platform.OS !== 'web' && href ? () => Linking.openURL(href) : onPress;
|
|
248
260
|
}
|
|
249
261
|
};
|
|
250
|
-
|
|
251
|
-
|
|
262
|
+
|
|
263
|
+
const getByViewport = propType => ({
|
|
252
264
|
xs: propType,
|
|
253
265
|
sm: propType,
|
|
254
266
|
md: propType,
|
|
255
267
|
lg: propType,
|
|
256
268
|
xl: propType
|
|
257
|
-
})
|
|
269
|
+
});
|
|
270
|
+
/**
|
|
271
|
+
* Utilities for props that allow different values to be applied at different viewports.
|
|
272
|
+
*
|
|
273
|
+
* These should apply viewport inheritance such that if a viewport is undefined, the value is
|
|
274
|
+
* taken from the next smallest viewport for which a value is available. For example, a
|
|
275
|
+
* responsive prop { xs: 2, lg: 3 } should apply 2 at sizes sm and md, and 3 at size xl.
|
|
276
|
+
*
|
|
277
|
+
* @property {Function} getByViewport - returns an object where each each viewport has a key
|
|
278
|
+
* containing the provided argument.
|
|
279
|
+
* @property {Function} getTypeByViewport - returns a PropTypes shape validator for an object where
|
|
280
|
+
* each viewport has a key containing the provided proptype.
|
|
281
|
+
* @property {Function} getTypeOptionallyByViewport - returns a PropTypes validator that accepts
|
|
282
|
+
* either the provided proptype on its own, or the output of getTypeByViewport
|
|
283
|
+
*/
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
export const responsiveProps = {
|
|
287
|
+
getByViewport,
|
|
288
|
+
getTypeByViewport: propType => PropTypes.shape(getByViewport(propType)),
|
|
289
|
+
getTypeOptionallyByViewport: propType => PropTypes.oneOfType([propType, PropTypes.shape(getByViewport(propType))])
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* @typedef {0|1|2|3|4|5|6|7|8|9|10|11} SpacingIndex - value used to select a size on the spacing scale
|
|
293
|
+
*
|
|
294
|
+
* @typedef SpacingOptions
|
|
295
|
+
* @property {VariantProp} [SpacingOptions.variant] - optional theme scale variants e.g. compact, wide
|
|
296
|
+
* @property {number} [SpacingOptions.size] - optional override to force a particular pixel size
|
|
297
|
+
* @property {number} [SpacingOptions.subtract] - optional number to subtract from final pixel size
|
|
298
|
+
*
|
|
299
|
+
* @typedef SpacingObject
|
|
300
|
+
* @property {SpacingIndex} [SpacingObject.xs] - space scale index to use above xs viewport
|
|
301
|
+
* @property {SpacingIndex} [SpacingObject.sm] - space scale index to use above sm viewport
|
|
302
|
+
* @property {SpacingIndex} [SpacingObject.md] - space scale index to use above md viewport
|
|
303
|
+
* @property {SpacingIndex} [SpacingObject.lg] - space scale index to use above lg viewport
|
|
304
|
+
* @property {SpacingIndex} [SpacingObject.xl] - space scale index to use above xl viewport
|
|
305
|
+
* @property {SpacingIndex} [SpacingObject.space] - space scale index to use at all viewports
|
|
306
|
+
* @property {SpacingOptions} [SpacingObject.options] - optional options for this spacing
|
|
307
|
+
*
|
|
308
|
+
* @typedef {SpacingIndex|SpacingObject} SpacingValue
|
|
309
|
+
*/
|
|
310
|
+
|
|
311
|
+
const spacingScale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
|
312
|
+
const spacingIndexPropType = PropTypes.oneOf(spacingScale);
|
|
313
|
+
const spacingObjectPropType = PropTypes.shape({ ...responsiveProps.getByViewport(spacingIndexPropType),
|
|
314
|
+
space: spacingIndexPropType,
|
|
315
|
+
options: PropTypes.shape({
|
|
316
|
+
variant: variantProp.propType,
|
|
317
|
+
size: PropTypes.number
|
|
318
|
+
})
|
|
319
|
+
});
|
|
320
|
+
/**
|
|
321
|
+
* Components and utilities that assign fixed space between components use a theme-defined spacing scale.
|
|
322
|
+
*
|
|
323
|
+
* They typically take one or more props of the {@link SpacingValue} type and turn it into a pixel size value
|
|
324
|
+
* using the hook `useSpacingScale`, which resolves any options or responsive behaviour and returns the
|
|
325
|
+
* appropriate value from the theme spacing scale.
|
|
326
|
+
*
|
|
327
|
+
* - see /ADRs/inter-component-spacing/README.md - ADR on this structure
|
|
328
|
+
* - see /src/utils/spacing/useSpacingScale.js - hook that processes spacing values
|
|
329
|
+
* - @see {@link SpacingIndex} - themes provide spacing scales of up to 12 sizes with optional theme rules.
|
|
330
|
+
* - @see {@link SpacingValue} - either a simple number referencing an index position on the spacing
|
|
331
|
+
* scale, or an object with an optional `options` key and one or more spacing indexes keyed either by
|
|
332
|
+
* viewports or `space` to apply at all viewports.
|
|
333
|
+
*/
|
|
334
|
+
|
|
335
|
+
export const spacingProps = {
|
|
336
|
+
scale: spacingScale,
|
|
337
|
+
types: {
|
|
338
|
+
spacingIndex: spacingIndexPropType,
|
|
339
|
+
spacingObject: spacingObjectPropType,
|
|
340
|
+
// Most spacing components and utilities take this prop / arg type:
|
|
341
|
+
spacingValue: PropTypes.oneOfType([spacingIndexPropType, spacingObjectPropType])
|
|
342
|
+
}
|
|
343
|
+
};
|
|
258
344
|
/**
|
|
259
345
|
* Returns a prop type validator which checks whether a prop is either a component or an array of components of a given
|
|
260
346
|
* type, based on their `name` property.
|
|
@@ -318,4 +404,13 @@ export const componentPropType = (passedName, checkDisplayName = false) => {
|
|
|
318
404
|
const validate = createValidate();
|
|
319
405
|
validate.isRequired = createValidate(true);
|
|
320
406
|
return validate;
|
|
407
|
+
};
|
|
408
|
+
export const copyPropTypes = PropTypes.oneOf(['en', 'fr']);
|
|
409
|
+
export const paddingProp = {
|
|
410
|
+
propType: PropTypes.shape({
|
|
411
|
+
paddingBottom: PropTypes.number,
|
|
412
|
+
paddingLeft: PropTypes.number,
|
|
413
|
+
paddingRight: PropTypes.number,
|
|
414
|
+
paddingTop: PropTypes.number
|
|
415
|
+
})
|
|
321
416
|
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { useViewport } from '../../ViewportProvider';
|
|
2
|
+
import { useThemeTokens } from '../../ThemeProvider';
|
|
3
|
+
import { resolveSpacingValue, resolveSpacingOptions } from './utils';
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('@telus-uds/system-constants/viewports').Viewport} Viewport
|
|
6
|
+
* @typedef {import('../propTypes.js').SpacingValue} SpacingValue
|
|
7
|
+
* @typedef {import('../propTypes.js').SpacingIndex} SpacingIndex
|
|
8
|
+
* @typedef {import('../propTypes.js').SpacingObject} SpacingObject
|
|
9
|
+
* @typedef {import('../propTypes.js').SpacingOptions} SpacingOptions
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Pass a {@link SpacingValue}, which is one of:
|
|
14
|
+
*
|
|
15
|
+
* - A number 0-11 ({@link SpacingIndex}) pointing to an index on the theme's spacing scale
|
|
16
|
+
* - Or, an object ({@link SpacingObject}), with optional properties:
|
|
17
|
+
* - `xs`, `sm`, `md`, `lg`, `xl`: {@link SpacingIndex} to apply at or above these specified {@link Viewport}
|
|
18
|
+
* - `options`: an optional {@link SpacingOptions} object, see below
|
|
19
|
+
* - `space`: a {@link SpacingIndex} to apply to all viewports (can be used alongside `options`)
|
|
20
|
+
*
|
|
21
|
+
* ## Example
|
|
22
|
+
*
|
|
23
|
+
* If the theme's spacing scale is `[0, 4, 8, 12, 16, 24, 48]` with no theme rules:
|
|
24
|
+
*
|
|
25
|
+
* - `useSpacingScale(0)` returns `0`
|
|
26
|
+
* - `useSpacingScale(2)` returns `8`
|
|
27
|
+
* - `useSpacingScale({ xs: 3, lg: 4 })` returns `12` at 'xs', 'sm' or 'md' viewports, and `16` at 'lg' or 'xl'.
|
|
28
|
+
*
|
|
29
|
+
* These viewport properties are intended to support case-specific responsive layout changes, for example, where a
|
|
30
|
+
* grid item drops or adds spacing on a particular side at viewports where the parent changes the layout shape.
|
|
31
|
+
*
|
|
32
|
+
* ## Theming
|
|
33
|
+
*
|
|
34
|
+
* A theme's `'spacingScale'` has theme rules and appearances same as components, and may support `viewport`.
|
|
35
|
+
* For example, a theme with the following rule would change index [2] above from `8` to `12` on large viewports:
|
|
36
|
+
*
|
|
37
|
+
* ```json
|
|
38
|
+
* { if: { space: 2, viewport: ['lg', 'xl'] }, tokens: { size: 12 }}
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* Setting responsive spacing in the theme is the preferred way to adapt the aesthetic tightness or looseness of
|
|
42
|
+
* a theme to the available space without changing the shape of the layout itself.
|
|
43
|
+
*
|
|
44
|
+
* ## Options
|
|
45
|
+
*
|
|
46
|
+
* Space values passed as objects may have an `options` key, with the following optional properties:
|
|
47
|
+
*
|
|
48
|
+
* - `variant`: Themes may choose to have spacing scale variants, same as variants in component themes.
|
|
49
|
+
* For example, if a theme rule contains `{ if: { space: 2, compact: true }, tokens: { size: 6 }}`,
|
|
50
|
+
* this tighter spacing can be accessed with:
|
|
51
|
+
*
|
|
52
|
+
* ```jsx
|
|
53
|
+
* const compactSize = useSpacingScale({ space: 2, options: { variant: { compact: true }}})`
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* - `subtract`: Sometimes on-brand spacing needs to be reduced by another value to achieve an on-brand result.
|
|
57
|
+
* For example, a component with a variable border may want to subtract its border width from its padding so
|
|
58
|
+
* the total distance from content edge to bounding box is a valid theme value, regardless of border width:
|
|
59
|
+
*
|
|
60
|
+
* ```jsx
|
|
61
|
+
* const padding = useSpacingScale({ space: 2, options: { subtract: themeTokens.borderWidth }})
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* - `size`: In exceptional cases, the theme's spacing scale may be bypassed by passing a `size` option.
|
|
65
|
+
* This can result in layouts that may be rejected for being off-brand so should only be used as a
|
|
66
|
+
* last resort for fixing layout problems (e.g. when working around non-branded embedded content).
|
|
67
|
+
* Where possible, fixing layout issues using a spacing scale value and the `subtract` option is preferred.
|
|
68
|
+
*
|
|
69
|
+
* ```jsx
|
|
70
|
+
* // Comments should be included when `size` is used, stating why this off-brand size is needed
|
|
71
|
+
* const iframeOffset = useSpacingScale({ options: { size: 13 }})`
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* ## References
|
|
75
|
+
*
|
|
76
|
+
* `/ADRs/inter-component-spacing/README.md` - ADR on this structure
|
|
77
|
+
*
|
|
78
|
+
* @param {SpacingValue} [spaceValue] - a {@link SpacingIndex} number, or a {@link SpacingObject}
|
|
79
|
+
* @returns {number}
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
const useSpacingScale = spaceValue => {
|
|
83
|
+
// In future, may need to consider window height as well as width, particularly for native apps,
|
|
84
|
+
// e.g. to ensure designs don't look lost on large, tall, not-so-wide portrait screens.
|
|
85
|
+
const viewport = useViewport();
|
|
86
|
+
const {
|
|
87
|
+
tokens,
|
|
88
|
+
variant,
|
|
89
|
+
overridden,
|
|
90
|
+
subtract = 0
|
|
91
|
+
} = resolveSpacingOptions(spaceValue);
|
|
92
|
+
const space = overridden ? null : resolveSpacingValue(spaceValue, viewport);
|
|
93
|
+
const {
|
|
94
|
+
size
|
|
95
|
+
} = useThemeTokens('spacingScale', tokens, variant, {
|
|
96
|
+
space,
|
|
97
|
+
viewport
|
|
98
|
+
});
|
|
99
|
+
return Math.max(size - subtract, 0);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export default useSpacingScale;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { viewports as systemViewports } from '@telus-uds/system-constants';
|
|
2
|
+
export const resolveSpacingValue = (space, viewport) => {
|
|
3
|
+
// If spacing value has been passed as undefined or nullish, get the 0-index
|
|
4
|
+
if (!space) return 0; // Pass through simple non-responsive numbers (which may be in objects alongside options)
|
|
5
|
+
|
|
6
|
+
if (typeof space === 'number') return space;
|
|
7
|
+
if (typeof space.space === 'number') return space.space; // Get the appropriate space value for the current viewport.
|
|
8
|
+
// If no viewports available (e.g. SSR), default to the smallest.
|
|
9
|
+
// If no values are found (e.g. empty space object), default to 0.
|
|
10
|
+
|
|
11
|
+
return (viewport && systemViewports.inherit(space)[viewport]) ?? space.xs ?? 0;
|
|
12
|
+
};
|
|
13
|
+
export const resolveSpacingOptions = space => {
|
|
14
|
+
if (!space?.options) return {};
|
|
15
|
+
const {
|
|
16
|
+
size,
|
|
17
|
+
variant,
|
|
18
|
+
subtract = 0
|
|
19
|
+
} = space.options;
|
|
20
|
+
const overridden = typeof size === 'number'; // Might need an option that adapts the size value by current user's system font scale, so that
|
|
21
|
+
// meaningful spacing between items doesn't disappear on the highest a11y font scale settings.
|
|
22
|
+
// https://github.com/telus/universal-design-system/issues/583
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
tokens: {
|
|
26
|
+
size
|
|
27
|
+
},
|
|
28
|
+
variant,
|
|
29
|
+
overridden,
|
|
30
|
+
subtract
|
|
31
|
+
};
|
|
32
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telus-uds/components-base",
|
|
3
|
-
"version": "0.0.2-prerelease.
|
|
3
|
+
"version": "0.0.2-prerelease.5",
|
|
4
4
|
"description": "Base components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"base"
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"lint": "telus-standard",
|
|
25
25
|
"lint:fix": "telus-standard --fix",
|
|
26
26
|
"format": "prettier --write .",
|
|
27
|
-
"build": "babel
|
|
27
|
+
"build": "babel src -d lib",
|
|
28
28
|
"dev": "yarn build --watch",
|
|
29
29
|
"release": "standard-version"
|
|
30
30
|
},
|
|
@@ -37,21 +37,18 @@
|
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"react": "*",
|
|
39
39
|
"react-native": "*",
|
|
40
|
-
"react-native-web": "
|
|
40
|
+
"react-native-web": "~0.14.13"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@babel/cli": "^7.9.0",
|
|
44
|
-
"@babel/plugin-transform-react-jsx": "^7.14.9",
|
|
45
43
|
"@testing-library/jest-native": "^4.0.1",
|
|
46
44
|
"@testing-library/react-hooks": "^7.0.1",
|
|
47
45
|
"@testing-library/react-native": "^7.2.0",
|
|
48
46
|
"react-test-renderer": "^16.3.2"
|
|
49
47
|
},
|
|
50
48
|
"dependencies": {
|
|
51
|
-
"@telus-uds/system-constants": "^0.0.2-prerelease.
|
|
52
|
-
"@telus-uds/tools-theme": "^0.0.2-prerelease.
|
|
49
|
+
"@telus-uds/system-constants": "^0.0.2-prerelease.1",
|
|
50
|
+
"@telus-uds/tools-theme": "^0.0.2-prerelease.3",
|
|
53
51
|
"lodash.merge": "^4.6.2",
|
|
54
52
|
"prop-types": "^15.7.2"
|
|
55
|
-
}
|
|
56
|
-
"gitHead": "a4930bde09eae51c0c77e78c8fd1bdeb918f5bd3"
|
|
53
|
+
}
|
|
57
54
|
}
|
package/release-context.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"previousReleaseTag": "@telus-uds/components-base/v0.0.2-prerelease.
|
|
3
|
-
"changelog": "### [0.0.2-prerelease.
|
|
4
|
-
"releaseTag": "@telus-uds/components-base/v0.0.2-prerelease.
|
|
5
|
-
"newVersion": "0.0.2-prerelease.
|
|
2
|
+
"previousReleaseTag": "@telus-uds/components-base/v0.0.2-prerelease.4",
|
|
3
|
+
"changelog": "### [0.0.2-prerelease.5](https://github.com/telus/universal-design-system/compare/@telus-uds/components-base/v0.0.2-prerelease.4...@telus-uds/components-base/v0.0.2-prerelease.5) (2021-10-27)\n\n\n### Features\n\n* **allium-web:** Add `ExpandCollapseMiniControl` component ([#661](https://github.com/telus/universal-design-system/issues/661)) ([227407e](https://github.com/telus/universal-design-system/commit/227407ec6a48c0a170b8e39761ba33293f13eb3c)), closes [#605](https://github.com/telus/universal-design-system/issues/605) [#605](https://github.com/telus/universal-design-system/issues/605) [#605](https://github.com/telus/universal-design-system/issues/605) [#605](https://github.com/telus/universal-design-system/issues/605) [#605](https://github.com/telus/universal-design-system/issues/605)\n* **allium:** add Allium `Card` ([#639](https://github.com/telus/universal-design-system/issues/639)) ([f88179c](https://github.com/telus/universal-design-system/commit/f88179c503dfe574bf7bac6bb36ce4726a9af338))\n* **base, allium:** add reverse directions and switch Allium card to using `StackView` ([#676](https://github.com/telus/universal-design-system/issues/676)) ([819a15f](https://github.com/telus/universal-design-system/commit/819a15f059faa47d1bc4e96c6370e9694effc003))\n* **base:** add StackView and Spacer ([#662](https://github.com/telus/universal-design-system/issues/662)) ([cc3965e](https://github.com/telus/universal-design-system/commit/cc3965e83c1ec2fa0f0dd006a03eeeeda5384940))\n* **base:** add StackWrap component ([#666](https://github.com/telus/universal-design-system/issues/666)) ([f34fb60](https://github.com/telus/universal-design-system/commit/f34fb60d0a923b1e4b3105c21b4a762b630ab309))\n* **base:** add TextInput component ([#649](https://github.com/telus/universal-design-system/issues/649)) ([245c073](https://github.com/telus/universal-design-system/commit/245c073ed3ba3a022f989d234fbf5cf972edec25))\n* **base:** add the Feedback component ([#656](https://github.com/telus/universal-design-system/issues/656)) ([5d7a5b6](https://github.com/telus/universal-design-system/commit/5d7a5b69c870ce1077adfdb230fddd1aa120b373))\n\n\n### Bug Fixes\n\n* **base:** control stretch from a row parent ([#668](https://github.com/telus/universal-design-system/issues/668)) ([fa13c37](https://github.com/telus/universal-design-system/commit/fa13c37b2bd6b4118dbeb39bc2cdf59d13d5b151))\n* **base:** fix icon link width in block container ([#645](https://github.com/telus/universal-design-system/issues/645)) ([ff60d1d](https://github.com/telus/universal-design-system/commit/ff60d1d1d0a66b7735b902098c7ca22e442265e1))\n",
|
|
4
|
+
"releaseTag": "@telus-uds/components-base/v0.0.2-prerelease.5",
|
|
5
|
+
"newVersion": "0.0.2-prerelease.5",
|
|
6
6
|
"packageName": "@telus-uds/components-base"
|
|
7
7
|
}
|
package/src/Box/Box.jsx
CHANGED
|
@@ -1,112 +1,149 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
import { View, ScrollView } from 'react-native'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const propToStyleMap = {
|
|
14
|
-
top: 'paddingTop',
|
|
15
|
-
below: 'marginBottom',
|
|
16
|
-
bottom: 'paddingBottom',
|
|
17
|
-
left: 'paddingLeft',
|
|
18
|
-
right: 'paddingRight',
|
|
19
|
-
vertical: 'paddingVertical',
|
|
20
|
-
horizontal: 'paddingHorizontal'
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const transformPropToStyle = (key) => propToStyleMap[key]
|
|
24
|
-
|
|
25
|
-
const getStyleByType = (currentViewPort, styleProp, levels) => {
|
|
26
|
-
if (typeof styleProp === 'undefined') return null
|
|
27
|
-
if (typeof styleProp === 'number') return levels[currentViewPort][styleProp]
|
|
28
|
-
const spacingValue = styleProp[currentViewPort]
|
|
29
|
-
return levels[currentViewPort][spacingValue]
|
|
30
|
-
}
|
|
4
|
+
import { useThemeTokens } from '../ThemeProvider'
|
|
5
|
+
import { a11yProps, spacingProps, variantProp, getTokensPropType, useSpacingScale } from '../utils'
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import('../utils/propTypes.js').SpacingValue} SpacingValue
|
|
8
|
+
* @typedef {import('../utils/propTypes.js').SpacingIndex} SpacingIndex
|
|
9
|
+
* @typedef {import('../utils/propTypes.js').SpacingObject} SpacingObject
|
|
10
|
+
* @typedef {import('../utils/propTypes.js').SpacingOptions} SpacingOptions
|
|
11
|
+
*/
|
|
31
12
|
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
13
|
+
const selectBoxStyles = (tokens) => {
|
|
14
|
+
const styles = { backgroundColor: tokens.backgroundColor }
|
|
15
|
+
const paddings = ['paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom']
|
|
16
|
+
// Only set on styles if token provided because we spread this object after the spacing scale values
|
|
17
|
+
paddings.forEach((side) => {
|
|
18
|
+
if (tokens[side]) {
|
|
19
|
+
styles[side] = tokens[side]
|
|
20
|
+
}
|
|
37
21
|
})
|
|
38
|
-
|
|
39
|
-
return styleObject
|
|
22
|
+
return styles
|
|
40
23
|
}
|
|
41
24
|
|
|
42
25
|
/**
|
|
43
|
-
* A layout utility component
|
|
26
|
+
* A layout utility component. Use Box to create space (padding) around content.
|
|
27
|
+
*
|
|
28
|
+
* ## Spacing
|
|
29
|
+
*
|
|
30
|
+
* For most simple uses, pass a number to Box's `space` prop referring to an index in the theme's
|
|
31
|
+
* spacing scale. For example, for a box with the theme's smallest non-zero amount of padding on all sides:
|
|
32
|
+
*
|
|
33
|
+
* ```jsx
|
|
34
|
+
* <Box space={1}>
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* #### Targetting specific sides
|
|
38
|
+
*
|
|
39
|
+
* Box allows spacing value props to be assigned to any side or all sides using the following props:
|
|
40
|
+
*
|
|
41
|
+
* - `space` sets the default for all sides, which is overridden by other props below
|
|
42
|
+
* - `horizontal` sets defaults for `left` and `right` (overriding `space` there if it is set)
|
|
43
|
+
* - `vertical` sets defaults for `top` and `bottom` (overriding `space` there if it is set)
|
|
44
|
+
* - `left` sets the left side padding, inside the box's bounds
|
|
45
|
+
* - `right` sets the right side padding, inside the box's bounds
|
|
46
|
+
* - `top` sets the top side padding, inside the box's bounds
|
|
47
|
+
* - `bottom` sets the bottom side padding, inside the box's bounds
|
|
48
|
+
*
|
|
49
|
+
* Box only controls spacing within its bounds. If space is needed around a box outside of its bounds,
|
|
50
|
+
* use `Spacer`, `StackView` or wrap the `Box` inside another `Box`.
|
|
44
51
|
*
|
|
45
|
-
*
|
|
52
|
+
* #### Viewport-specific spacing
|
|
53
|
+
*
|
|
54
|
+
* Responsive behaviours may be set by passing any of the above props an object keyed by viewports.
|
|
55
|
+
*
|
|
56
|
+
* For example, if a theme's spacing scale is [0, 4, 8...], this below will have padding of 4px on top,
|
|
57
|
+
* bottom, and right, and its left padding will be 0px on xs, sm and md viewports and 8px on lg and xl viewports:
|
|
58
|
+
*
|
|
59
|
+
* ```jsx
|
|
60
|
+
* <Box space={1} left={{ xs: 0, lg: 2 }} />`
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* #### Subtracting from spacing
|
|
64
|
+
*
|
|
65
|
+
* A parent may sometimes need to reduce the spacing size on one or more sides of a `Box` by some variable.
|
|
66
|
+
* For example, the parent may have a border on one side and want to reduce the spacing of a child box
|
|
67
|
+
* by the width of that border on that side. This can be achieved using the `subtract` option of the
|
|
68
|
+
* spacing object, for example:
|
|
69
|
+
*
|
|
70
|
+
* ```jsx
|
|
71
|
+
* <Box space={2} left={{ space: 2, options: { subtract: themeTokens.borderWidthLeft } }} />`
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* See `useSpacingScale` hook for other spacing value options and documentation.
|
|
75
|
+
*
|
|
76
|
+
* ## Theming
|
|
77
|
+
*
|
|
78
|
+
* Box is intended for layout, so minimal theming is supported. Use components like `Card` for
|
|
79
|
+
* more sophisticated theming.
|
|
80
|
+
*
|
|
81
|
+
* ## Scroll
|
|
82
|
+
*
|
|
83
|
+
* If passed, the box will be scrollable. If an object is passed, it will be passed to React Native's
|
|
84
|
+
* `ScrollView` component as props.
|
|
85
|
+
*
|
|
86
|
+
* When building native iOS and Android apps, it is important to remember to ensure any screen containing
|
|
87
|
+
* text content is inside a scrollable box, as screens are not scrollable by default and even very
|
|
88
|
+
* short text will require scrolling on small devices at the highest accessibility text scaling settings.
|
|
46
89
|
*/
|
|
47
90
|
const Box = ({
|
|
91
|
+
space,
|
|
92
|
+
horizontal = space,
|
|
93
|
+
vertical = space,
|
|
94
|
+
top = vertical,
|
|
95
|
+
bottom = vertical,
|
|
96
|
+
left = horizontal,
|
|
97
|
+
right = horizontal,
|
|
48
98
|
children,
|
|
49
|
-
top,
|
|
50
|
-
below,
|
|
51
|
-
bottom,
|
|
52
|
-
left,
|
|
53
|
-
right,
|
|
54
|
-
vertical,
|
|
55
|
-
horizontal,
|
|
56
99
|
variant,
|
|
57
100
|
tokens,
|
|
58
|
-
scroll
|
|
59
|
-
|
|
101
|
+
scroll,
|
|
102
|
+
testID,
|
|
103
|
+
...rest
|
|
60
104
|
}) => {
|
|
61
|
-
const
|
|
105
|
+
const a11y = a11yProps.select(rest)
|
|
62
106
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const { backgroundColor } = useThemeTokens('Box', tokens, variant)
|
|
71
|
-
const bkgStyle = { backgroundColor }
|
|
72
|
-
|
|
73
|
-
const styleProps = {
|
|
74
|
-
below,
|
|
75
|
-
bottom,
|
|
76
|
-
left,
|
|
77
|
-
right,
|
|
78
|
-
top,
|
|
79
|
-
vertical,
|
|
80
|
-
horizontal
|
|
107
|
+
const themeTokens = useThemeTokens('Box', tokens, variant)
|
|
108
|
+
const styles = {
|
|
109
|
+
paddingLeft: useSpacingScale(left),
|
|
110
|
+
paddingRight: useSpacingScale(right),
|
|
111
|
+
paddingTop: useSpacingScale(top),
|
|
112
|
+
paddingBottom: useSpacingScale(bottom),
|
|
113
|
+
...selectBoxStyles(themeTokens)
|
|
81
114
|
}
|
|
82
|
-
const baseStyle = getBaseStyle(viewport, styleProps, levels)
|
|
83
|
-
const styles = [baseStyle, bkgStyle]
|
|
84
115
|
|
|
85
116
|
if (scroll) {
|
|
117
|
+
const scrollProps = typeof scroll === 'object' ? scroll : {}
|
|
118
|
+
scrollProps.contentContainerStyle = [styles, scrollProps.contentContainerStyle]
|
|
86
119
|
return (
|
|
87
|
-
<ScrollView
|
|
120
|
+
<ScrollView {...scrollProps} {...a11y} testID={testID}>
|
|
88
121
|
{children}
|
|
89
122
|
</ScrollView>
|
|
90
123
|
)
|
|
91
124
|
}
|
|
92
|
-
return
|
|
125
|
+
return (
|
|
126
|
+
<View {...a11y} style={styles} testID={testID}>
|
|
127
|
+
{children}
|
|
128
|
+
</View>
|
|
129
|
+
)
|
|
93
130
|
}
|
|
94
131
|
|
|
95
|
-
// TODO apply props as separate
|
|
96
|
-
const responsiveLevelsPropType = responsivePropTypeFactory(levelsPropType)
|
|
97
|
-
|
|
98
132
|
Box.propTypes = {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
scroll: PropTypes.
|
|
107
|
-
|
|
133
|
+
space: spacingProps.types.spacingValue,
|
|
134
|
+
vertical: spacingProps.types.spacingValue,
|
|
135
|
+
horizontal: spacingProps.types.spacingValue,
|
|
136
|
+
bottom: spacingProps.types.spacingValue,
|
|
137
|
+
left: spacingProps.types.spacingValue,
|
|
138
|
+
right: spacingProps.types.spacingValue,
|
|
139
|
+
top: spacingProps.types.spacingValue,
|
|
140
|
+
scroll: PropTypes.oneOfType([
|
|
141
|
+
PropTypes.bool,
|
|
142
|
+
ScrollView.propTypes ? PropTypes.shape(ScrollView.propTypes) : PropTypes.object
|
|
143
|
+
]),
|
|
108
144
|
variant: variantProp.propType,
|
|
109
145
|
tokens: getTokensPropType('Box'),
|
|
146
|
+
testID: PropTypes.string,
|
|
110
147
|
children: PropTypes.node.isRequired
|
|
111
148
|
}
|
|
112
149
|
|