Skip to content

Commit 5b075a4

Browse files
ToMESSKabalzss
authored andcommitted
feat(ui-toggle-details): rework ToggleDetails
INSTUI-4815
1 parent c02be10 commit 5b075a4

11 files changed

Lines changed: 488 additions & 19 deletions

File tree

packages/ui-toggle-details/package.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,18 +74,18 @@
7474
"default": "./es/exports/a.js"
7575
},
7676
"./v11_7": {
77-
"src": "./src/exports/a.ts",
78-
"types": "./types/exports/a.d.ts",
79-
"import": "./es/exports/a.js",
80-
"require": "./lib/exports/a.js",
81-
"default": "./es/exports/a.js"
77+
"src": "./src/exports/b.ts",
78+
"types": "./types/exports/b.d.ts",
79+
"import": "./es/exports/b.js",
80+
"require": "./lib/exports/b.js",
81+
"default": "./es/exports/b.js"
8282
},
8383
"./latest": {
84-
"src": "./src/exports/a.ts",
85-
"types": "./types/exports/a.d.ts",
86-
"import": "./es/exports/a.js",
87-
"require": "./lib/exports/a.js",
88-
"default": "./es/exports/a.js"
84+
"src": "./src/exports/b.ts",
85+
"types": "./types/exports/b.d.ts",
86+
"import": "./es/exports/b.js",
87+
"require": "./lib/exports/b.js",
88+
"default": "./es/exports/b.js"
8989
}
9090
}
9191
}

packages/ui-toggle-details/src/ToggleDetails/v1/props.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import type {
2929
ToggleDetailsTheme
3030
} from '@instructure/shared-types'
3131
import type { WithStyleProps, ComponentStyle } from '@instructure/emotion'
32-
import type { ViewProps } from '@instructure/ui-view'
32+
import type { ViewProps } from '@instructure/ui-view/v11_6'
3333

3434
type ToggleDetailsOwnProps = {
3535
variant?: 'default' | 'filled'

packages/ui-toggle-details/src/ToggleDetails/v2/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import generateStyle from './styles'
3737
import type { ToggleDetailsProps } from './props'
3838
import { allowedProps } from './props'
3939
import type { ExpandableToggleProps } from '@instructure/ui-expandable'
40-
import type { ViewProps } from '@instructure/ui-view/v11_6'
40+
import type { ViewProps } from '@instructure/ui-view/latest'
4141

4242
/**
4343
---

packages/ui-toggle-details/src/ToggleDetails/v2/props.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import type {
2929
ToggleDetailsTheme
3030
} from '@instructure/shared-types'
3131
import type { WithStyleProps, ComponentStyle } from '@instructure/emotion'
32-
import type { ViewProps } from '@instructure/ui-view'
32+
import type { ViewProps } from '@instructure/ui-view/latest'
3333

3434
type ToggleDetailsOwnProps = {
3535
variant?: 'default' | 'filled'

packages/ui-toggle-details/src/ToggleGroup/v1/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ import {
3030
getElementType,
3131
callRenderProp
3232
} from '@instructure/ui-react-utils'
33-
import { IconButton } from '@instructure/ui-buttons/latest'
33+
import { IconButton } from '@instructure/ui-buttons/v11_6'
3434
import { Transition } from '@instructure/ui-motion'
3535
import { Expandable } from '@instructure/ui-expandable'
3636
import type { ExpandableToggleProps } from '@instructure/ui-expandable'
3737
import { isActiveElement } from '@instructure/ui-dom-utils'
38-
import { Flex } from '@instructure/ui-flex/latest'
39-
import { View } from '@instructure/ui-view/latest'
38+
import { Flex } from '@instructure/ui-flex/v11_6'
39+
import { View } from '@instructure/ui-view/v11_6'
4040
import {
4141
IconArrowOpenEndSolid,
4242
IconArrowOpenDownSolid
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
describes: ToggleGroup
3+
---
4+
5+
Performs the same function as [`ToggleDetails`](ToggleDetails), but with the summary separated from the
6+
toggle button, and built in padding and borders around the summary and main content area.
7+
8+
### Basic example
9+
10+
```javascript
11+
---
12+
type: example
13+
---
14+
<ToggleGroup
15+
toggleLabel="This is the toggle button label for screenreaders"
16+
summary="This is the summary"
17+
background="default"
18+
>
19+
<View display="block" padding="small">Here is the expanded content</View>
20+
</ToggleGroup>
21+
```
22+
23+
### More detailed examples
24+
25+
#### `defaultExpanded` to make the component `expanded` when it renders
26+
27+
```javascript
28+
---
29+
type: example
30+
---
31+
<ToggleGroup
32+
toggleLabel="This is the toggle button label for screenreaders"
33+
summary="This is the summary"
34+
defaultExpanded
35+
>
36+
<View display="block" padding="small">This content is expanded when the component renders</View>
37+
</ToggleGroup>
38+
```
39+
40+
#### Passing in your own `icon` and `iconExpanded`
41+
42+
```javascript
43+
---
44+
type: example
45+
---
46+
<ToggleGroup
47+
toggleLabel="This is the toggle button label for screenreaders"
48+
summary="This is the summary"
49+
iconExpanded={XInstUIIcon}
50+
icon={PlusInstUIIcon}
51+
>
52+
<View display="block" padding="small">Here is the expanded content</View>
53+
</ToggleGroup>
54+
```
55+
56+
#### Disable default transition of details
57+
58+
```javascript
59+
---
60+
type: example
61+
---
62+
<ToggleGroup
63+
transition={false}
64+
toggleLabel="This is the toggle button label for screenreaders"
65+
summary="This is the summary"
66+
>
67+
<View display="block" padding="small">This content will not fade in</View>
68+
</ToggleGroup>
69+
```
70+
71+
#### Disable default border if you want to nest ToggleGroups
72+
73+
```javascript
74+
---
75+
type: example
76+
---
77+
<ToggleGroup
78+
defaultExpanded
79+
toggleLabel="This is the toggle button label for screenreaders"
80+
summary={<Heading level="h3">Parent ToggleGroup</Heading>}
81+
>
82+
<ToggleGroup
83+
size="small"
84+
toggleLabel="This is the toggle button label for screenreaders"
85+
summary="I am nested inside a parent ToggleGroup"
86+
border={false}
87+
>
88+
<View display="block" padding="small">
89+
This is the details section of the nested ToggleGroup
90+
</View>
91+
</ToggleGroup>
92+
</ToggleGroup>
93+
```

packages/ui-toggle-details/src/ToggleGroup/v1/__tests__/ToggleGroup.test.tsx renamed to packages/ui-toggle-details/src/ToggleGroup/v2/__tests__/ToggleGroup.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,13 +217,14 @@ describe('<ToggleGroup />', () => {
217217
</ToggleGroup>
218218
)
219219
const toggle = container.querySelector('button')!
220-
const svg = container.querySelector('svg')!
220+
let svg = container.querySelector('svg')!
221221

222222
expect(svg).toHaveTextContent('Icon collapsed')
223223

224224
await userEvent.click(toggle)
225225

226226
await waitFor(() => {
227+
svg = container.querySelector('svg')!
227228
expect(svg).toHaveTextContent('Icon expanded')
228229
})
229230
})
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2015 - present Instructure, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
import { Component } from 'react'
26+
27+
import {
28+
omitProps,
29+
pickProps,
30+
getElementType
31+
} from '@instructure/ui-react-utils'
32+
import { IconButton } from '@instructure/ui-buttons/latest'
33+
import { Transition } from '@instructure/ui-motion'
34+
import { Expandable } from '@instructure/ui-expandable'
35+
import type { ExpandableToggleProps } from '@instructure/ui-expandable'
36+
import { isActiveElement } from '@instructure/ui-dom-utils'
37+
import { Flex } from '@instructure/ui-flex/latest'
38+
import { View } from '@instructure/ui-view/latest'
39+
import {
40+
ChevronRightInstUIIcon,
41+
ChevronDownInstUIIcon,
42+
renderIconWithProps
43+
} from '@instructure/ui-icons'
44+
import type { ToggleGroupProps } from './props'
45+
import { allowedProps } from './props'
46+
47+
import { withStyle } from '@instructure/emotion'
48+
49+
import generateStyle from './styles'
50+
51+
const toggleGroupSizeToIconSize = {
52+
small: 'md',
53+
medium: 'md',
54+
large: 'lg'
55+
} as const
56+
57+
/**
58+
---
59+
category: components
60+
---
61+
**/
62+
@withStyle(generateStyle)
63+
class ToggleGroup extends Component<ToggleGroupProps> {
64+
static readonly componentId = 'ToggleGroup'
65+
66+
static allowedProps = allowedProps
67+
68+
static defaultProps = {
69+
size: 'medium',
70+
icon: ChevronRightInstUIIcon,
71+
iconExpanded: ChevronDownInstUIIcon,
72+
defaultExpanded: false,
73+
transition: true,
74+
as: 'span',
75+
border: true
76+
} as const
77+
78+
ref: Element | null = null
79+
_button: HTMLElement | null = null
80+
_shouldTransition = false
81+
82+
handleRef = (el: Element | null) => {
83+
const { elementRef } = this.props
84+
85+
this.ref = el
86+
87+
if (typeof elementRef === 'function') {
88+
elementRef(el)
89+
}
90+
}
91+
92+
handleButtonRef = (el: Element | null) => {
93+
this._button = el as HTMLElement
94+
}
95+
96+
get focused() {
97+
return isActiveElement(this._button)
98+
}
99+
100+
focus() {
101+
this._button?.focus()
102+
}
103+
104+
componentDidMount() {
105+
this._shouldTransition = true
106+
}
107+
108+
componentDidUpdate() {
109+
this.props.makeStyles?.(this.state)
110+
}
111+
112+
renderIcon(expanded: boolean) {
113+
const Icon = expanded ? this.props.iconExpanded : this.props.icon
114+
if (!Icon) return null
115+
116+
const iconSize = toggleGroupSizeToIconSize[this.props.size!]
117+
return renderIconWithProps(Icon, iconSize, 'baseColor')
118+
}
119+
120+
renderToggle(
121+
toggleProps: ReturnType<ExpandableToggleProps>,
122+
expanded: boolean
123+
) {
124+
const { toggleLabel, size } = this.props
125+
let label
126+
if (typeof toggleLabel === 'function') {
127+
label = toggleLabel(expanded)
128+
} else {
129+
label = toggleLabel
130+
}
131+
132+
const props = { ...toggleProps } as Record<string, any>
133+
return (
134+
<IconButton
135+
{...props}
136+
withBackground={false}
137+
withBorder={false}
138+
size={size === 'large' ? 'medium' : 'small'}
139+
elementRef={this.handleButtonRef}
140+
screenReaderLabel={label}
141+
>
142+
{this.renderIcon(expanded)}
143+
</IconButton>
144+
)
145+
}
146+
147+
renderDetails(detailsProps: { id: string }) {
148+
const { styles } = this.props
149+
return (
150+
<View
151+
{...detailsProps}
152+
display="block"
153+
borderWidth="small none none none"
154+
borderColor={styles?.borderColor}
155+
>
156+
{this.props.transition && this._shouldTransition ? (
157+
<Transition transitionOnMount in type="fade">
158+
{this.props.children}
159+
</Transition>
160+
) : (
161+
this.props.children
162+
)}
163+
</View>
164+
)
165+
}
166+
167+
render() {
168+
const Element = getElementType(ToggleGroup, this.props)
169+
const { styles } = this.props
170+
return (
171+
<Expandable {...pickProps(this.props, Expandable.allowedProps)}>
172+
{({ expanded, getToggleProps, getDetailsProps }) => {
173+
return (
174+
<View
175+
{...omitProps(this.props, ToggleGroup.allowedProps)}
176+
borderWidth={this.props.border ? 'small' : 'none'}
177+
as={Element}
178+
elementRef={this.handleRef}
179+
display="block"
180+
borderRadius="medium"
181+
background="primary"
182+
borderColor={styles?.borderColor}
183+
data-cid="ToggleGroup"
184+
>
185+
<Flex
186+
padding={
187+
this.props.size === 'small'
188+
? 'x-small'
189+
: 'small small small x-small'
190+
}
191+
>
192+
<Flex.Item>
193+
{this.renderToggle(getToggleProps(), expanded)}
194+
</Flex.Item>
195+
<Flex.Item shouldGrow shouldShrink padding="0 0 0 x-small">
196+
{this.props.summary}
197+
</Flex.Item>
198+
</Flex>
199+
{expanded ? (
200+
this.renderDetails(getDetailsProps())
201+
) : (
202+
<span {...getDetailsProps()} />
203+
)}
204+
</View>
205+
)
206+
}}
207+
</Expandable>
208+
)
209+
}
210+
}
211+
212+
export default ToggleGroup
213+
export { ToggleGroup }

0 commit comments

Comments
 (0)