Skip to content

Commit 9cf8b8c

Browse files
committed
feat(many): add margin prop to FormFieldGroup, CheckboxGroup, RadioInputGroup, Checkbox, RadioInput, RangeInput, Text, and ToggleButton
1 parent 5184d8c commit 9cf8b8c

File tree

28 files changed

+9489
-6173
lines changed

28 files changed

+9489
-6173
lines changed

package-lock.json

Lines changed: 9026 additions & 6154 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/shared-types/src/ComponentThemeVariables.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,7 @@ export type RadioInputTheme = {
12031203
toggleSmallFontSize: Typography['fontSizeXSmall']
12041204
toggleMediumFontSize: Typography['fontSizeSmall']
12051205
toggleLargeFontSize: Typography['fontSizeMedium']
1206+
spacing: any // TODO remove any
12061207
}
12071208

12081209
export type RangeInputTheme = {
@@ -1460,9 +1461,9 @@ export type TextTheme = Typography & {
14601461
alertColor: string
14611462
warningColor: string
14621463
aiColor: string
1463-
14641464
aiBackgroundColor: string
14651465
paragraphMargin: string | 0
1466+
spacing: any // TODO remove any
14661467
}
14671468

14681469
export type TextAreaTheme = {

packages/ui-badge/src/Badge/__tests__/Badge.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ describe('<Badge />', () => {
9090
expect(badge).not.toBeNull()
9191
expect(badgeStyle).not.toBeNull()
9292
expect(badgeStyle).toHaveProperty('bottom')
93-
expect(badgeStyle).toHaveProperty('bottom', 'calc(-5px)')
93+
expect(badgeStyle).toHaveProperty('bottom', 'calc(-1 * 5px)')
9494
expect(badgeStyle).toHaveProperty('inset-inline-start', 'calc(-1 * 5px)')
9595
})
9696

packages/ui-buttons/src/ToggleButton/__tests__/ToggleButton.test.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,4 +197,59 @@ describe('<ToggleButton />', () => {
197197
expect(onClick).toHaveBeenCalledTimes(1)
198198
})
199199
})
200+
201+
describe('margin prop', () => {
202+
it('should apply margin with theme tokens', () => {
203+
const { container } = render(
204+
<div>
205+
<ToggleButton
206+
screenReaderLabel="test1"
207+
renderIcon={icon}
208+
renderTooltipContent="tooltip"
209+
status="unpressed"
210+
margin="medium"
211+
/>
212+
<ToggleButton
213+
screenReaderLabel="test2"
214+
renderIcon={icon}
215+
renderTooltipContent="tooltip"
216+
status="unpressed"
217+
margin="large"
218+
/>
219+
<ToggleButton
220+
screenReaderLabel="test3"
221+
renderIcon={icon}
222+
renderTooltipContent="tooltip"
223+
status="unpressed"
224+
margin="space4"
225+
/>
226+
<ToggleButton
227+
screenReaderLabel="test4"
228+
renderIcon={icon}
229+
renderTooltipContent="tooltip"
230+
status="unpressed"
231+
margin="small medium"
232+
/>
233+
</div>
234+
)
235+
236+
const allButtons = container.querySelectorAll('button')
237+
238+
const buttonMedium = allButtons[0] as HTMLElement
239+
const buttonMediumStyle = window.getComputedStyle(buttonMedium)
240+
const buttonLarge = allButtons[1] as HTMLElement
241+
const buttonLargeStyle = window.getComputedStyle(buttonLarge)
242+
const buttonSpace = allButtons[2] as HTMLElement
243+
const buttonSpaceStyle = window.getComputedStyle(buttonSpace)
244+
const buttonMixed = allButtons[3] as HTMLElement
245+
const buttonMixedStyle = window.getComputedStyle(buttonMixed)
246+
247+
// Note: ToggleButton passes margin to IconButton, which uses View component
248+
// View only accepts theme tokens, not custom CSS values
249+
expect(buttonMediumStyle.margin).toBe('1.5rem')
250+
expect(buttonLargeStyle.margin).toBe('2.25rem')
251+
expect(buttonSpaceStyle.margin).toBe('0.25rem')
252+
expect(buttonMixedStyle.margin).toBe('0.75rem 1.5rem')
253+
})
254+
})
200255
})

packages/ui-buttons/src/ToggleButton/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ class ToggleButton extends Component<ToggleButtonProps, ToggleButtonState> {
9090
status,
9191
placement,
9292
onClick,
93+
margin,
9394
...props
9495
} = this.props
9596

@@ -122,6 +123,7 @@ class ToggleButton extends Component<ToggleButtonProps, ToggleButtonState> {
122123
interaction={interaction}
123124
aria-pressed={status === 'pressed'}
124125
data-cid="ToggleButton"
126+
margin={margin}
125127
>
126128
{callRenderProp(renderIcon)}
127129
</IconButton>

packages/ui-buttons/src/ToggleButton/props.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import type {
3535
} from '@instructure/ui-position'
3636
import type { ViewProps } from '@instructure/ui-view'
3737
import { Renderable } from '@instructure/shared-types'
38+
import type { Spacing } from '@instructure/emotion'
3839

3940
type ToggleButtonOwnProps = {
4041
/**
@@ -110,6 +111,11 @@ type ToggleButtonOwnProps = {
110111
* or a function returning an element.
111112
*/
112113
constrain?: PositionConstraint
114+
115+
/**
116+
* Margin around the component. Accepts a `Spacing` token. See token values and example usage in [this guide](https://instructure.design/#layout-spacing).
117+
*/
118+
margin?: Spacing
113119
}
114120

115121
type PropKeys = keyof ToggleButtonOwnProps
@@ -129,6 +135,7 @@ const allowedProps: AllowedPropKeys = [
129135
'elementRef',
130136
'interaction',
131137
'isShowingTooltip',
138+
'margin',
132139
'mountNode',
133140
'onClick',
134141
'placement',

packages/ui-checkbox/src/Checkbox/__tests__/Checkbox.test.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,4 +245,33 @@ describe('<Checkbox />', () => {
245245
expect(axeCheck).toBe(true)
246246
})
247247
})
248+
249+
describe('margin prop', () => {
250+
it('should apply margin with custom CSS value and with tokens', () => {
251+
const { container } = render(
252+
<div>
253+
<Checkbox label="test1" margin="30px" />
254+
<Checkbox label="test2" margin="large" />
255+
<Checkbox label="test3" margin="space4" />
256+
<Checkbox label="test4" margin="small 20px" />
257+
</div>
258+
)
259+
260+
const allCheckboxes = container.querySelectorAll('[data-cid="Checkbox"]')
261+
262+
const checkboxCustom = allCheckboxes[0] as HTMLElement
263+
const checkboxCustomStyle = window.getComputedStyle(checkboxCustom)
264+
const checkboxLarge = allCheckboxes[1] as HTMLElement
265+
const checkboxLargeStyle = window.getComputedStyle(checkboxLarge)
266+
const checkboxSpace = allCheckboxes[2] as HTMLElement
267+
const checkboxSpaceStyle = window.getComputedStyle(checkboxSpace)
268+
const checkboxMixed = allCheckboxes[3] as HTMLElement
269+
const checkboxMixedStyle = window.getComputedStyle(checkboxMixed)
270+
271+
expect(checkboxCustomStyle.margin).toBe('30px')
272+
expect(checkboxLargeStyle.margin).toBe('2.25rem')
273+
expect(checkboxSpaceStyle.margin).toBe('0.25rem')
274+
expect(checkboxMixedStyle.margin).toBe('0.75rem 20px')
275+
})
276+
})
248277
})

packages/ui-checkbox/src/Checkbox/props.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ import type {
2828
OtherHTMLAttributes,
2929
ToggleFacadeTheme
3030
} from '@instructure/shared-types'
31-
import type { WithStyleProps, ComponentStyle } from '@instructure/emotion'
31+
import type {
32+
WithStyleProps,
33+
ComponentStyle,
34+
Spacing
35+
} from '@instructure/emotion'
3236
import type { WithDeterministicIdProps } from '@instructure/ui-react-utils'
3337

3438
type CheckboxOwnProps = {
@@ -74,6 +78,10 @@ type CheckboxOwnProps = {
7478
inline?: boolean
7579
labelPlacement?: 'top' | 'start' | 'end'
7680
isRequired?: boolean
81+
/**
82+
* Margin around the component. Accepts a `Spacing` token. See token values and example usage in [this guide](https://instructure.design/#layout-spacing).
83+
*/
84+
margin?: Spacing
7785
/**
7886
* A function that provides a reference to the actual underlying input element
7987
*/
@@ -119,6 +127,7 @@ const allowedProps: AllowedPropKeys = [
119127
'inline',
120128
'labelPlacement',
121129
'isRequired',
130+
'margin',
122131
'inputRef'
123132
]
124133

packages/ui-checkbox/src/Checkbox/styles.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import type { CheckboxProps, CheckboxStyle } from './props'
2626
import type { ComponentTheme } from '@instructure/shared-types'
27+
import { mapSpacingToShorthand } from '@instructure/emotion'
2728

2829
/**
2930
* ---
@@ -39,7 +40,16 @@ const generateStyle = (
3940
componentTheme: ComponentTheme,
4041
props: CheckboxProps
4142
): CheckboxStyle => {
42-
const { inline, disabled } = props
43+
const { inline, disabled, margin } = props
44+
45+
// Calculate margin and support both theme token values like "space4" and custom values like "10px"
46+
const cssMargin = margin
47+
? mapSpacingToShorthand(
48+
margin,
49+
(componentTheme as ComponentTheme & { spacing: Record<string, string> })
50+
.spacing
51+
)
52+
: undefined
4353

4454
return {
4555
requiredInvalid: {
@@ -49,12 +59,13 @@ const generateStyle = (
4959
paddingLeft: componentTheme.checkErrorInsetWidth
5060
},
5161
indentedToggleError: {
52-
paddingLeft: componentTheme.toggleErrorInsetWidth,
62+
paddingLeft: componentTheme.toggleErrorInsetWidth
5363
},
5464
checkbox: {
5565
label: 'checkbox',
5666
position: 'relative',
5767
width: '100%',
68+
...(margin && { margin: cssMargin }),
5869
...(disabled && {
5970
cursor: 'not-allowed',
6071
pointerEvents: 'none',

packages/ui-checkbox/src/Checkbox/theme.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const generateComponentTheme = (theme: Theme): any => {
3636
requiredInvalidColor: colors?.contrasts?.red5782,
3737
toggleErrorInsetWidth: `calc(${forms?.inputHeightSmall}*1.5 + ${spacing?.small})`,
3838
checkErrorInsetWidth: `calc(1.25em + ${spacing?.xSmall})`,
39+
spacing: spacing
3940
}
4041

4142
return {

0 commit comments

Comments
 (0)