@instructure/ui-drilldown 8.21.1-snapshot.10
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/LICENSE.md +27 -0
- package/README.md +30 -0
- package/es/Drilldown/DrilldownGroup/index.js +56 -0
- package/es/Drilldown/DrilldownGroup/props.js +42 -0
- package/es/Drilldown/DrilldownLocator.js +144 -0
- package/es/Drilldown/DrilldownOption/index.js +59 -0
- package/es/Drilldown/DrilldownOption/props.js +46 -0
- package/es/Drilldown/DrilldownPage/index.js +56 -0
- package/es/Drilldown/DrilldownPage/props.js +41 -0
- package/es/Drilldown/DrilldownSeparator/index.js +52 -0
- package/es/Drilldown/DrilldownSeparator/props.js +30 -0
- package/es/Drilldown/__examples__/Drilldown.examples.js +299 -0
- package/es/Drilldown/index.js +1241 -0
- package/es/Drilldown/locator.js +26 -0
- package/es/Drilldown/props.js +67 -0
- package/es/Drilldown/styles.js +82 -0
- package/es/Drilldown/theme.js +43 -0
- package/es/index.js +24 -0
- package/lib/Drilldown/DrilldownGroup/index.js +42 -0
- package/lib/Drilldown/DrilldownGroup/props.js +56 -0
- package/lib/Drilldown/DrilldownLocator.js +157 -0
- package/lib/Drilldown/DrilldownOption/index.js +45 -0
- package/lib/Drilldown/DrilldownOption/props.js +57 -0
- package/lib/Drilldown/DrilldownPage/index.js +66 -0
- package/lib/Drilldown/DrilldownPage/props.js +56 -0
- package/lib/Drilldown/DrilldownSeparator/index.js +38 -0
- package/lib/Drilldown/DrilldownSeparator/props.js +41 -0
- package/lib/Drilldown/__examples__/Drilldown.examples.js +289 -0
- package/lib/Drilldown/index.js +1253 -0
- package/lib/Drilldown/locator.js +40 -0
- package/lib/Drilldown/props.js +83 -0
- package/lib/Drilldown/styles.js +90 -0
- package/lib/Drilldown/theme.js +51 -0
- package/lib/index.js +13 -0
- package/lib/package.json +1 -0
- package/package.json +58 -0
- package/src/Drilldown/DrilldownGroup/index.tsx +64 -0
- package/src/Drilldown/DrilldownGroup/props.ts +149 -0
- package/src/Drilldown/DrilldownLocator.ts +101 -0
- package/src/Drilldown/DrilldownOption/index.tsx +68 -0
- package/src/Drilldown/DrilldownOption/props.ts +224 -0
- package/src/Drilldown/DrilldownPage/index.tsx +58 -0
- package/src/Drilldown/DrilldownPage/props.ts +129 -0
- package/src/Drilldown/DrilldownSeparator/index.tsx +60 -0
- package/src/Drilldown/DrilldownSeparator/props.ts +65 -0
- package/src/Drilldown/README.md +1515 -0
- package/src/Drilldown/__examples__/Drilldown.examples.tsx +318 -0
- package/src/Drilldown/index.tsx +1492 -0
- package/src/Drilldown/locator.ts +28 -0
- package/src/Drilldown/props.ts +378 -0
- package/src/Drilldown/styles.ts +95 -0
- package/src/Drilldown/theme.ts +47 -0
- package/src/index.ts +26 -0
- package/tsconfig.build.json +30 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +4 -0
- package/types/Drilldown/DrilldownGroup/index.d.ts +59 -0
- package/types/Drilldown/DrilldownGroup/index.d.ts.map +1 -0
- package/types/Drilldown/DrilldownGroup/props.d.ts +65 -0
- package/types/Drilldown/DrilldownGroup/props.d.ts.map +1 -0
- package/types/Drilldown/DrilldownLocator.d.ts +652 -0
- package/types/Drilldown/DrilldownLocator.d.ts.map +1 -0
- package/types/Drilldown/DrilldownOption/index.d.ts +134 -0
- package/types/Drilldown/DrilldownOption/index.d.ts.map +1 -0
- package/types/Drilldown/DrilldownOption/props.d.ts +116 -0
- package/types/Drilldown/DrilldownOption/props.d.ts.map +1 -0
- package/types/Drilldown/DrilldownPage/index.d.ts +45 -0
- package/types/Drilldown/DrilldownPage/index.d.ts.map +1 -0
- package/types/Drilldown/DrilldownPage/props.d.ts +51 -0
- package/types/Drilldown/DrilldownPage/props.d.ts.map +1 -0
- package/types/Drilldown/DrilldownSeparator/index.d.ts +27 -0
- package/types/Drilldown/DrilldownSeparator/index.d.ts.map +1 -0
- package/types/Drilldown/DrilldownSeparator/props.d.ts +18 -0
- package/types/Drilldown/DrilldownSeparator/props.d.ts.map +1 -0
- package/types/Drilldown/__examples__/Drilldown.examples.d.ts +5 -0
- package/types/Drilldown/__examples__/Drilldown.examples.d.ts.map +1 -0
- package/types/Drilldown/index.d.ts +246 -0
- package/types/Drilldown/index.d.ts.map +1 -0
- package/types/Drilldown/locator.d.ts +4 -0
- package/types/Drilldown/locator.d.ts.map +1 -0
- package/types/Drilldown/props.d.ts +189 -0
- package/types/Drilldown/props.d.ts.map +1 -0
- package/types/Drilldown/styles.d.ts +15 -0
- package/types/Drilldown/styles.d.ts.map +1 -0
- package/types/Drilldown/theme.d.ts +10 -0
- package/types/Drilldown/theme.d.ts.map +1 -0
- package/types/index.d.ts +3 -0
- package/types/index.d.ts.map +1 -0
|
@@ -0,0 +1,1515 @@
|
|
|
1
|
+
---
|
|
2
|
+
describes: Drilldown
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
`Drilldown` is a diverse component that displays hierarchical data in a fashion that allows the users to "drill down" and dig deeper into the layers (pages) of the data structure. It has similar look and features to the [Menu](#Menu), [Select](#Select) and [TreeBrowser](#TreeBrowser) components.
|
|
6
|
+
|
|
7
|
+
The `Drilldown` component exists to support navigating and managing tree structures in compact spaces. With WCAG 2.1 requirements around small viewports, and also with general responsiveness standards the classic tree-navigation and flyout menu patterns got outdated.
|
|
8
|
+
|
|
9
|
+
Some of Drilldown's features include:
|
|
10
|
+
|
|
11
|
+
- options that navigate (drill) down in the structure is marked with an arrow;
|
|
12
|
+
- options that navigate back in the structure is always a “back” option at the top of the list;
|
|
13
|
+
- option groups can have titles that have the standard InstUI menu group title style;
|
|
14
|
+
- groups are divided by separator lines;
|
|
15
|
+
- items in groups have no indent unless the group is a checkbox or radio option group, in which case the selected items are marked with a "check" icon, all unselected items have an indent;
|
|
16
|
+
- secondary information blocks can be displayed both in-line with the option, or below the option with a dedicated text and color style;
|
|
17
|
+
- the component can be rendered both in-line and in a popover.
|
|
18
|
+
|
|
19
|
+
### Pages
|
|
20
|
+
|
|
21
|
+
The main building blocks of Drilldown are the `Drilldown.Pages`. These represent the layers of the structure and can contain Options, Separators and Groups. Each page has a "header" that can contain a page title, the back navigation and a "page action" option (see [Page header section](/#Drilldown/#page-header)).
|
|
22
|
+
|
|
23
|
+
Each page needs an `id` prop that is used in the navigation. Options point to pages with their `subPageId` prop, and the Drilldown itself needs a `rootPageId` that indicates the first root level page, which renders first.
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
---
|
|
27
|
+
example: true
|
|
28
|
+
---
|
|
29
|
+
<Drilldown rootPageId='root' width="20rem" maxHeight="30rem">
|
|
30
|
+
<Drilldown.Page id="root" renderTitle='Groceries'>
|
|
31
|
+
<Drilldown.Option id="Produce" subPageId="Produce">
|
|
32
|
+
Produce
|
|
33
|
+
</Drilldown.Option>
|
|
34
|
+
<Drilldown.Option id="Grains and Bread" subPageId="Grains and Bread">
|
|
35
|
+
Grains and Bread
|
|
36
|
+
</Drilldown.Option>
|
|
37
|
+
</Drilldown.Page>
|
|
38
|
+
|
|
39
|
+
<Drilldown.Page id="Produce" renderTitle='Produce'>
|
|
40
|
+
<Drilldown.Option id="Fruits" subPageId="Fruits">
|
|
41
|
+
Fruits
|
|
42
|
+
</Drilldown.Option>
|
|
43
|
+
<Drilldown.Option id="Vegtables" subPageId="Vegtables">
|
|
44
|
+
Vegtables
|
|
45
|
+
</Drilldown.Option>
|
|
46
|
+
</Drilldown.Page>
|
|
47
|
+
|
|
48
|
+
<Drilldown.Page id="Grains and Bread" renderTitle='Grains and Bread'>
|
|
49
|
+
{['Pasta', 'Rice', 'Bread', 'Flour', 'Cereal', 'Oats'].map(
|
|
50
|
+
item => <Drilldown.Option id={item} key={item}>{item}</Drilldown.Option>
|
|
51
|
+
)}
|
|
52
|
+
</Drilldown.Page>
|
|
53
|
+
|
|
54
|
+
<Drilldown.Page id="Fruits" renderTitle='Fruits'>
|
|
55
|
+
{['Apple', 'Orange', 'Cherry', 'Grapefruit', 'Mango', 'Banana', 'Strawberry'].map(
|
|
56
|
+
item => <Drilldown.Option id={item} key={item}>{item}</Drilldown.Option>
|
|
57
|
+
)}
|
|
58
|
+
</Drilldown.Page>
|
|
59
|
+
|
|
60
|
+
<Drilldown.Page id="Vegtables" renderTitle='Vegtables'>
|
|
61
|
+
{['Tomato', 'Cucumber', 'Eggplant', 'Lettuce', 'Garlic', 'Onion', 'Corn', 'Carrot', 'Bell pepper'].map(
|
|
62
|
+
item => <Drilldown.Option id={item} key={item}>{item}</Drilldown.Option>
|
|
63
|
+
)}
|
|
64
|
+
</Drilldown.Page>
|
|
65
|
+
</Drilldown>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Options
|
|
69
|
+
|
|
70
|
+
`Drilldown.Option` is the main child component of Drilldown. The content can be a ReactNode or a function returning a ReactNode. The function has an object as its parameter, containing the option's `id`, `variant` and `isSelected` state.
|
|
71
|
+
|
|
72
|
+
> Note: Drilldown is based on the [Options](#Options) component, so the Drilldown.Options are rendered as `Options.Item` components under the hood. This is why the `variant` parameter has the values of Options.Item's `variant` prop.
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
---
|
|
76
|
+
example: true
|
|
77
|
+
---
|
|
78
|
+
<Drilldown rootPageId='root' width="20rem" maxHeight="30rem">
|
|
79
|
+
<Drilldown.Page id="root">
|
|
80
|
+
<Drilldown.Option id="option1">
|
|
81
|
+
Option
|
|
82
|
+
</Drilldown.Option>
|
|
83
|
+
<Drilldown.Option id="option2">
|
|
84
|
+
{(props) => `Option ${props.variant === 'highlighted'
|
|
85
|
+
? 'is highlighted'
|
|
86
|
+
: 'is not highlighted'}`}
|
|
87
|
+
</Drilldown.Option>
|
|
88
|
+
<Drilldown.Option id="option3">
|
|
89
|
+
<Pill>Pill</Pill> Option
|
|
90
|
+
</Drilldown.Option>
|
|
91
|
+
<Drilldown.Option id="option4" disabled>
|
|
92
|
+
Option
|
|
93
|
+
</Drilldown.Option>
|
|
94
|
+
</Drilldown.Page>
|
|
95
|
+
</Drilldown>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Options can be links too. If the `href` prop is set, the option renders as an `<a>` element.
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
---
|
|
102
|
+
example: true
|
|
103
|
+
---
|
|
104
|
+
<Drilldown rootPageId='root' width="20rem" maxHeight="30rem">
|
|
105
|
+
<Drilldown.Page id="root">
|
|
106
|
+
<Drilldown.Option id="option1" href="/#Options">
|
|
107
|
+
Options component
|
|
108
|
+
</Drilldown.Option>
|
|
109
|
+
<Drilldown.Option id="option2"href="/#Menu">
|
|
110
|
+
Menu component
|
|
111
|
+
</Drilldown.Option>
|
|
112
|
+
<Drilldown.Option id="option3" href="/#Select">
|
|
113
|
+
Options component
|
|
114
|
+
</Drilldown.Option>
|
|
115
|
+
<Drilldown.Option id="option4" href="/#SimpleSelect" disabled>
|
|
116
|
+
SimpleSelect component
|
|
117
|
+
</Drilldown.Option>
|
|
118
|
+
<Drilldown.Option id="option5" href="/#TreeBrowser">
|
|
119
|
+
TreeBrowser component
|
|
120
|
+
</Drilldown.Option>
|
|
121
|
+
</Drilldown.Page>
|
|
122
|
+
</Drilldown>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Just like `Options.Item`, `Drilldown.Option` can render a description under the label and icons before or after the label.
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
---
|
|
129
|
+
example: true
|
|
130
|
+
---
|
|
131
|
+
<Drilldown rootPageId='root' width="20rem" maxHeight="30rem">
|
|
132
|
+
<Drilldown.Page id="root">
|
|
133
|
+
<Drilldown.Option
|
|
134
|
+
id="option1"
|
|
135
|
+
description="Curabitur fringilla, urna ut efficitur molestie, nibh lacus tincidunt elit, ut tempor ipsum nunc sit amet massa."
|
|
136
|
+
renderBeforeLabel={IconCheckSolid}
|
|
137
|
+
renderAfterLabel={IconArrowOpenEndSolid}
|
|
138
|
+
beforeLabelContentVAlign="start"
|
|
139
|
+
afterLabelContentVAlign="start"
|
|
140
|
+
>
|
|
141
|
+
Option
|
|
142
|
+
</Drilldown.Option>
|
|
143
|
+
<Drilldown.Option
|
|
144
|
+
id="option2"
|
|
145
|
+
description="Curabitur fringilla, urna ut efficitur molestie, nibh lacus tincidunt elit, ut tempor ipsum nunc sit amet massa."
|
|
146
|
+
renderBeforeLabel={IconCheckSolid}
|
|
147
|
+
renderAfterLabel={IconArrowOpenEndSolid}
|
|
148
|
+
beforeLabelContentVAlign="center"
|
|
149
|
+
afterLabelContentVAlign="center"
|
|
150
|
+
>
|
|
151
|
+
Option
|
|
152
|
+
</Drilldown.Option>
|
|
153
|
+
<Drilldown.Option
|
|
154
|
+
id="option3"
|
|
155
|
+
description="Curabitur fringilla, urna ut efficitur molestie, nibh lacus tincidunt elit, ut tempor ipsum nunc sit amet massa."
|
|
156
|
+
renderBeforeLabel={IconCheckSolid}
|
|
157
|
+
renderAfterLabel={IconArrowOpenEndSolid}
|
|
158
|
+
beforeLabelContentVAlign="end"
|
|
159
|
+
afterLabelContentVAlign="end"
|
|
160
|
+
>
|
|
161
|
+
Option
|
|
162
|
+
</Drilldown.Option>
|
|
163
|
+
</Drilldown.Page>
|
|
164
|
+
</Drilldown>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Additionally, the `renderLabelInfo` prop can render text or other elements next to the label.
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
---
|
|
171
|
+
example: true
|
|
172
|
+
render: false
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
class VideoSettingsExample extends React.Component {
|
|
176
|
+
state = {
|
|
177
|
+
selectedCaption: 'English',
|
|
178
|
+
selectedSpeed: 'Normal',
|
|
179
|
+
selectedQuality: '720p',
|
|
180
|
+
isCommentsOn: true
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
renderTrigger() {
|
|
184
|
+
return (
|
|
185
|
+
<Button renderIcon={IconSettingsSolid}>
|
|
186
|
+
Video Settings
|
|
187
|
+
</Button>
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
renderSelected(props, value) {
|
|
192
|
+
return (
|
|
193
|
+
<span style={{ color: props.variant === 'default' ? '#6B7780' : undefined }}>
|
|
194
|
+
{value}
|
|
195
|
+
</span>
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
renderSelectGroup(options, stateName) {
|
|
200
|
+
return (
|
|
201
|
+
<Drilldown.Group
|
|
202
|
+
id={`${stateName}Group`}
|
|
203
|
+
selectableType="single"
|
|
204
|
+
defaultSelected={[this.state[stateName]]}
|
|
205
|
+
onSelect={(_event, { value }) => {
|
|
206
|
+
this.setState({ [stateName]: value[0] })
|
|
207
|
+
}}
|
|
208
|
+
>
|
|
209
|
+
{options.map((option, idx) => (
|
|
210
|
+
<Drilldown.Option
|
|
211
|
+
key={idx}
|
|
212
|
+
id={option}
|
|
213
|
+
value={option}
|
|
214
|
+
onOptionClick={(_event, { goToPreviousPage }) => {
|
|
215
|
+
goToPreviousPage()
|
|
216
|
+
}}
|
|
217
|
+
>
|
|
218
|
+
{option}
|
|
219
|
+
</Drilldown.Option>
|
|
220
|
+
))}
|
|
221
|
+
</Drilldown.Group>
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
render() {
|
|
226
|
+
const {
|
|
227
|
+
captionOptions,
|
|
228
|
+
speedOptions,
|
|
229
|
+
qualityOptions
|
|
230
|
+
} = this.props
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<Drilldown
|
|
234
|
+
rootPageId="videoSettings"
|
|
235
|
+
width="18.5rem"
|
|
236
|
+
maxHeight="26.5rem"
|
|
237
|
+
shouldHideOnSelect={false}
|
|
238
|
+
trigger={this.renderTrigger()}
|
|
239
|
+
onToggle={(_event, { shown, goToPage }) => {
|
|
240
|
+
shown && goToPage('videoSettings')
|
|
241
|
+
}}
|
|
242
|
+
>
|
|
243
|
+
<Drilldown.Page
|
|
244
|
+
id="videoSettings"
|
|
245
|
+
renderTitle="Video Settings"
|
|
246
|
+
withoutHeaderSeparator
|
|
247
|
+
>
|
|
248
|
+
<Drilldown.Option
|
|
249
|
+
id="captions"
|
|
250
|
+
subPageId="captions"
|
|
251
|
+
renderLabelInfo={
|
|
252
|
+
(props) => this.renderSelected(props, this.state.selectedCaption)
|
|
253
|
+
}
|
|
254
|
+
>
|
|
255
|
+
Captions
|
|
256
|
+
</Drilldown.Option>
|
|
257
|
+
<Drilldown.Option
|
|
258
|
+
id="speed"
|
|
259
|
+
subPageId="speed"
|
|
260
|
+
renderLabelInfo={
|
|
261
|
+
(props) => this.renderSelected(props, this.state.selectedSpeed)
|
|
262
|
+
}
|
|
263
|
+
>
|
|
264
|
+
Speed
|
|
265
|
+
</Drilldown.Option>
|
|
266
|
+
<Drilldown.Option
|
|
267
|
+
id="quality"
|
|
268
|
+
subPageId="quality"
|
|
269
|
+
renderLabelInfo={
|
|
270
|
+
(props) => this.renderSelected(props, this.state.selectedQuality)
|
|
271
|
+
}
|
|
272
|
+
>
|
|
273
|
+
Quality
|
|
274
|
+
</Drilldown.Option>
|
|
275
|
+
<Drilldown.Option
|
|
276
|
+
id="comments"
|
|
277
|
+
themeOverride={{ padding: '0.25rem 0.75rem' }}
|
|
278
|
+
afterLabelContentVAlign="center"
|
|
279
|
+
onOptionClick={() => {
|
|
280
|
+
this.setState((state) => ({ isCommentsOn: !state.isCommentsOn }))
|
|
281
|
+
}}
|
|
282
|
+
role="checkbox"
|
|
283
|
+
aria-checked={this.state.isCommentsOn ? 'true' : 'false'}
|
|
284
|
+
// prevents reading the label of the checkbox too (duplicated)
|
|
285
|
+
aria-describedby={['']}
|
|
286
|
+
|
|
287
|
+
renderLabelInfo={
|
|
288
|
+
<Checkbox
|
|
289
|
+
label={<ScreenReaderContent>Comments</ScreenReaderContent>}
|
|
290
|
+
variant='toggle'
|
|
291
|
+
readOnly
|
|
292
|
+
checked={this.state.isCommentsOn}
|
|
293
|
+
onChange={() => {
|
|
294
|
+
// needed for controlled Checkbox,
|
|
295
|
+
// but the state is handled on the Drilldown.Option
|
|
296
|
+
}}
|
|
297
|
+
labelPlacement="start"
|
|
298
|
+
tabIndex={-1}
|
|
299
|
+
/>
|
|
300
|
+
}
|
|
301
|
+
>
|
|
302
|
+
Comments
|
|
303
|
+
</Drilldown.Option>
|
|
304
|
+
</Drilldown.Page>
|
|
305
|
+
|
|
306
|
+
<Drilldown.Page
|
|
307
|
+
id="captions"
|
|
308
|
+
renderTitle="Captions"
|
|
309
|
+
>
|
|
310
|
+
{this.renderSelectGroup(captionOptions, 'selectedCaption')}
|
|
311
|
+
</Drilldown.Page>
|
|
312
|
+
|
|
313
|
+
<Drilldown.Page
|
|
314
|
+
id="speed"
|
|
315
|
+
renderTitle="Speed"
|
|
316
|
+
>
|
|
317
|
+
{this.renderSelectGroup(speedOptions, 'selectedSpeed')}
|
|
318
|
+
</Drilldown.Page>
|
|
319
|
+
|
|
320
|
+
<Drilldown.Page
|
|
321
|
+
id="quality"
|
|
322
|
+
renderTitle="Quality"
|
|
323
|
+
>
|
|
324
|
+
{this.renderSelectGroup(qualityOptions, 'selectedQuality')}
|
|
325
|
+
</Drilldown.Page>
|
|
326
|
+
</Drilldown>
|
|
327
|
+
)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
render(<VideoSettingsExample
|
|
332
|
+
captionOptions={['Off', 'English', 'German', 'Spanish', 'Durtch']}
|
|
333
|
+
speedOptions={['0.5x', 'Normal', '1.25x', '1.5x', '2x']}
|
|
334
|
+
qualityOptions={['Auto', '360p', '480p', '720p', '1080p']}
|
|
335
|
+
/>)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Displaying Drilldown in a Popover
|
|
339
|
+
|
|
340
|
+
Just like [Menu](#Menu), Drilldown accepts a `trigger` property: it will render a toggle button which, when clicked, shows or hides the Drilldown in a [Popover](#Popover). Drilldown passes many of its props to Popover in this case (`mountNode`, `shouldContainFocus`, `withArrow`, etc.).
|
|
341
|
+
|
|
342
|
+
```js
|
|
343
|
+
---
|
|
344
|
+
example: true
|
|
345
|
+
render: false
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
class SelectContactsExample extends React.Component {
|
|
349
|
+
state = {
|
|
350
|
+
selected: []
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
getCategoryById(id) {
|
|
354
|
+
return this.props.contactsData.find(cat => cat.id === id)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
selectContacts(values) {
|
|
358
|
+
this.setState({ selected: values })
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
renderContacts(contacts) {
|
|
362
|
+
return contacts.map((contact, idx) => {
|
|
363
|
+
const { id, name, email } = contact
|
|
364
|
+
|
|
365
|
+
return (
|
|
366
|
+
<Drilldown.Option
|
|
367
|
+
key={idx}
|
|
368
|
+
id={id}
|
|
369
|
+
value={name}
|
|
370
|
+
description={
|
|
371
|
+
<TruncateText>
|
|
372
|
+
#{idx + 1} | {email}
|
|
373
|
+
</TruncateText>
|
|
374
|
+
}
|
|
375
|
+
onOptionClick={() => {
|
|
376
|
+
this.selectContacts([contact])
|
|
377
|
+
}}
|
|
378
|
+
>
|
|
379
|
+
{name}
|
|
380
|
+
</Drilldown.Option>
|
|
381
|
+
)
|
|
382
|
+
})
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
renderSubCategoryOptions(subCategories = []) {
|
|
386
|
+
return subCategories.map((subCatId, idx) => {
|
|
387
|
+
const category = this.getCategoryById(subCatId)
|
|
388
|
+
const { id, title } = category
|
|
389
|
+
|
|
390
|
+
return (
|
|
391
|
+
<Drilldown.Option key={idx} id={id} subPageId={id}>
|
|
392
|
+
{title}
|
|
393
|
+
</Drilldown.Option>
|
|
394
|
+
)
|
|
395
|
+
})
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
renderPage(category, key) {
|
|
399
|
+
const { id, title, subCategories = [], options = [] } = category
|
|
400
|
+
|
|
401
|
+
const allContacts = this.getAllContactsFromCategory(category)
|
|
402
|
+
|
|
403
|
+
return (
|
|
404
|
+
<Drilldown.Page
|
|
405
|
+
key={key}
|
|
406
|
+
id={id}
|
|
407
|
+
renderTitle={title}
|
|
408
|
+
renderActionLabel={`Send to All (${allContacts.length})`}
|
|
409
|
+
onHeaderActionClicked={() => {
|
|
410
|
+
this.selectContacts(allContacts)
|
|
411
|
+
}}
|
|
412
|
+
>
|
|
413
|
+
{[
|
|
414
|
+
...this.renderSubCategoryOptions(subCategories),
|
|
415
|
+
...this.renderContacts(options)
|
|
416
|
+
]}
|
|
417
|
+
</Drilldown.Page>
|
|
418
|
+
)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
getAllContactsFromCategory(category) {
|
|
422
|
+
let allContacts = []
|
|
423
|
+
|
|
424
|
+
const addOptions = (cat) => {
|
|
425
|
+
const { subCategories = [], options = [] } = cat
|
|
426
|
+
allContacts.push(...options)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
subCategories.forEach((subCatId) => {
|
|
430
|
+
addOptions(this.getCategoryById(subCatId))
|
|
431
|
+
})
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
addOptions(category)
|
|
435
|
+
return allContacts
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
render() {
|
|
439
|
+
const { contactsData } = this.props
|
|
440
|
+
const { selected = [] } = this.state
|
|
441
|
+
|
|
442
|
+
return (
|
|
443
|
+
<Flex alignItems='start'>
|
|
444
|
+
<Flex.Item width="20rem" textAlign="center" padding='x-small 0 0'>
|
|
445
|
+
<Drilldown
|
|
446
|
+
rootPageId="contacts"
|
|
447
|
+
width="18.5rem"
|
|
448
|
+
maxHeight="26.5rem"
|
|
449
|
+
trigger={<Button>Select Contacts</Button>}
|
|
450
|
+
onToggle={(_event, args) => {
|
|
451
|
+
args.shown && this.selectContacts([])
|
|
452
|
+
}}
|
|
453
|
+
>
|
|
454
|
+
{contactsData.map((page, idx) => this.renderPage(page, idx))}
|
|
455
|
+
</Drilldown>
|
|
456
|
+
</Flex.Item>
|
|
457
|
+
|
|
458
|
+
<Flex.Item padding='0 0 0 large' shouldGrow shouldShrink>
|
|
459
|
+
<View as="div" padding='small' background='primary'>
|
|
460
|
+
<div>
|
|
461
|
+
<Text weight='bold'>Selected ({selected ? selected.length : 0}):</Text>
|
|
462
|
+
</div>
|
|
463
|
+
<div>
|
|
464
|
+
{selected.map((a) => a.name).join(', ')}
|
|
465
|
+
</div>
|
|
466
|
+
</View>
|
|
467
|
+
</Flex.Item>
|
|
468
|
+
</Flex>
|
|
469
|
+
)
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const generateCategory = (name, count) => {
|
|
474
|
+
return Array(count)
|
|
475
|
+
.fill(name)
|
|
476
|
+
.map((category, idx) => {
|
|
477
|
+
const id = category.replace(/(-|\s)/g, '').toLowerCase()
|
|
478
|
+
return {
|
|
479
|
+
id: `${id}${idx + 1}`,
|
|
480
|
+
category,
|
|
481
|
+
name: `${category} ${idx + 1}`,
|
|
482
|
+
email: `${id}${idx + 1}@randommail.com`
|
|
483
|
+
}
|
|
484
|
+
})
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const contactsData = [
|
|
488
|
+
{
|
|
489
|
+
id: 'contacts',
|
|
490
|
+
title: 'Contacts',
|
|
491
|
+
subCategories: ['administrators', 'teachers', 'students']
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
id: 'administrators',
|
|
495
|
+
title: 'Administrators',
|
|
496
|
+
subCategories: ['accountAdmins', 'subAccountAdmins']
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
id: 'accountAdmins',
|
|
500
|
+
title: 'Account Admins',
|
|
501
|
+
options: generateCategory('Account Admin', 8)
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
id: 'subAccountAdmins',
|
|
505
|
+
title: 'Sub-Account Admins',
|
|
506
|
+
options: generateCategory('Sub-Account Admin', 12)
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
id: 'teachers',
|
|
510
|
+
title: 'Teachers',
|
|
511
|
+
options: generateCategory('Teacher', 23)
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
id: 'students',
|
|
515
|
+
title: 'Students',
|
|
516
|
+
options: generateCategory('Student', 34)
|
|
517
|
+
}
|
|
518
|
+
]
|
|
519
|
+
|
|
520
|
+
render(<SelectContactsExample contactsData={contactsData} />)
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Page header
|
|
524
|
+
|
|
525
|
+
Each page can have a "header" with three optional items. The header and the Drilldown content are separated with a `<Drilldown.Separator>` (can be hidden with the `withoutHeaderSeparator` prop.)
|
|
526
|
+
|
|
527
|
+
##### Title
|
|
528
|
+
|
|
529
|
+
The `renderTitle` prop sets the title of the Page.
|
|
530
|
+
|
|
531
|
+
##### Action
|
|
532
|
+
|
|
533
|
+
The `renderActionLabel` displays an "action" option that can be used as e.g.: a "Select all" function. The action itself can be set with the `onHeaderActionClicked` prop.
|
|
534
|
+
|
|
535
|
+
##### Back navigation
|
|
536
|
+
|
|
537
|
+
On every page (except the root page) a "Back" navigation option is displayed with an arrow.
|
|
538
|
+
|
|
539
|
+
The label can be changed with `renderBackButtonLabel` prop (defaults to "Back"). If a function is passed, it has a `prevPageTitle` parameter.
|
|
540
|
+
|
|
541
|
+
This option will always display on the page when needed and cannot be disabled.
|
|
542
|
+
|
|
543
|
+
```js
|
|
544
|
+
---
|
|
545
|
+
example: true
|
|
546
|
+
render: false
|
|
547
|
+
---
|
|
548
|
+
class BackNavigationExample extends React.Component {
|
|
549
|
+
state = {
|
|
550
|
+
showTitle: true,
|
|
551
|
+
showAction: true,
|
|
552
|
+
showAlternativeBackLabel: false
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
render() {
|
|
556
|
+
const { showTitle, showAction, showAlternativeBackLabel } = this.state
|
|
557
|
+
|
|
558
|
+
return (
|
|
559
|
+
<Flex alignItems='start'>
|
|
560
|
+
<Flex.Item>
|
|
561
|
+
<Drilldown rootPageId='root' width="20rem" maxHeight="30rem">
|
|
562
|
+
<Drilldown.Page
|
|
563
|
+
id="root"
|
|
564
|
+
renderTitle={showTitle && 'Root page'}
|
|
565
|
+
renderActionLabel={showAction && 'Action!'}
|
|
566
|
+
>
|
|
567
|
+
<Drilldown.Option id="option1" subPageId='secondPage'>
|
|
568
|
+
Option
|
|
569
|
+
</Drilldown.Option>
|
|
570
|
+
<Drilldown.Option id="option2" subPageId='secondPage'>
|
|
571
|
+
Option
|
|
572
|
+
</Drilldown.Option>
|
|
573
|
+
<Drilldown.Option id="option3" subPageId='secondPage'>
|
|
574
|
+
Option
|
|
575
|
+
</Drilldown.Option>
|
|
576
|
+
</Drilldown.Page>
|
|
577
|
+
|
|
578
|
+
<Drilldown.Page
|
|
579
|
+
id="secondPage"
|
|
580
|
+
renderTitle={showTitle && 'Second page'}
|
|
581
|
+
renderActionLabel={showAction && 'Action!'}
|
|
582
|
+
renderBackButtonLabel={showAlternativeBackLabel
|
|
583
|
+
? ((prevPageTitle) => prevPageTitle ? `Back to ${prevPageTitle}` : 'Back')
|
|
584
|
+
: undefined}
|
|
585
|
+
>
|
|
586
|
+
<Drilldown.Option id="option1">Option</Drilldown.Option>
|
|
587
|
+
<Drilldown.Option id="option2">Option</Drilldown.Option>
|
|
588
|
+
<Drilldown.Option id="option3">Option</Drilldown.Option>
|
|
589
|
+
</Drilldown.Page>
|
|
590
|
+
</Drilldown>
|
|
591
|
+
</Flex.Item>
|
|
592
|
+
|
|
593
|
+
<Flex.Item padding="0 0 0 large" shouldGrow shouldShrink>
|
|
594
|
+
<FormFieldGroup description="Drilldown Group example settings">
|
|
595
|
+
<Checkbox
|
|
596
|
+
checked={!this.state.showTitle ? false : this.state.showAlternativeBackLabel}
|
|
597
|
+
disabled={!this.state.showTitle}
|
|
598
|
+
label='Display previous page title in back navigation ("Show page title" needs to be turned on)'
|
|
599
|
+
variant="toggle"
|
|
600
|
+
onChange={
|
|
601
|
+
() => {
|
|
602
|
+
this.setState({ showAlternativeBackLabel: !this.state.showAlternativeBackLabel })
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
/>
|
|
606
|
+
<Checkbox
|
|
607
|
+
checked={this.state.showTitle}
|
|
608
|
+
label="Show page title"
|
|
609
|
+
variant="toggle"
|
|
610
|
+
onChange={
|
|
611
|
+
() => {
|
|
612
|
+
this.setState({ showTitle: !this.state.showTitle })
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
/>
|
|
616
|
+
<Checkbox
|
|
617
|
+
checked={this.state.showAction}
|
|
618
|
+
label="Show action label"
|
|
619
|
+
variant="toggle"
|
|
620
|
+
onChange={
|
|
621
|
+
() => {
|
|
622
|
+
this.setState({ showAction: !this.state.showAction })
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
/>
|
|
626
|
+
</FormFieldGroup>
|
|
627
|
+
</Flex.Item>
|
|
628
|
+
</Flex>
|
|
629
|
+
)
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
render(<BackNavigationExample />)
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
### Option Groups
|
|
637
|
+
|
|
638
|
+
Wrapping the Options in `<Drilldown.Group>` will separate these options with separators. These separators can be hidden, and you can provide a label with the `renderGroupTitle` prop.
|
|
639
|
+
|
|
640
|
+
```js
|
|
641
|
+
---
|
|
642
|
+
example: true
|
|
643
|
+
render: false
|
|
644
|
+
---
|
|
645
|
+
class GroupsExample extends React.Component {
|
|
646
|
+
state = {
|
|
647
|
+
showSeparators: true,
|
|
648
|
+
showTitles: true
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
render() {
|
|
652
|
+
return (
|
|
653
|
+
<Flex alignItems='start'>
|
|
654
|
+
<Flex.Item>
|
|
655
|
+
<Drilldown rootPageId='root' width="20rem" maxHeight="30rem">
|
|
656
|
+
<Drilldown.Page id="root">
|
|
657
|
+
<Drilldown.Group
|
|
658
|
+
id="italy"
|
|
659
|
+
renderGroupTitle={this.state.showTitles && 'Italy'}
|
|
660
|
+
withoutSeparators={!this.state.showSeparators}
|
|
661
|
+
>
|
|
662
|
+
<Drilldown.Option id="item11">Milano</Drilldown.Option>
|
|
663
|
+
<Drilldown.Option id="item12">Florence</Drilldown.Option>
|
|
664
|
+
</Drilldown.Group>
|
|
665
|
+
|
|
666
|
+
<Drilldown.Group
|
|
667
|
+
id="france"
|
|
668
|
+
renderGroupTitle={this.state.showTitles && 'France'}
|
|
669
|
+
withoutSeparators={!this.state.showSeparators}
|
|
670
|
+
>
|
|
671
|
+
<Drilldown.Option id="item21">Lyon</Drilldown.Option>
|
|
672
|
+
<Drilldown.Option id="item22">Bordeaux</Drilldown.Option>
|
|
673
|
+
</Drilldown.Group>
|
|
674
|
+
|
|
675
|
+
<Drilldown.Group
|
|
676
|
+
id="germany"
|
|
677
|
+
renderGroupTitle={this.state.showTitles && 'Germany'}
|
|
678
|
+
withoutSeparators={!this.state.showSeparators}
|
|
679
|
+
>
|
|
680
|
+
<Drilldown.Option id="item31">Frankfurt</Drilldown.Option>
|
|
681
|
+
<Drilldown.Option id="item32">Dortmund</Drilldown.Option>
|
|
682
|
+
</Drilldown.Group>
|
|
683
|
+
</Drilldown.Page>
|
|
684
|
+
</Drilldown>
|
|
685
|
+
</Flex.Item>
|
|
686
|
+
|
|
687
|
+
<Flex.Item padding="0 0 0 large">
|
|
688
|
+
<FormFieldGroup description="Drilldown Group example settings">
|
|
689
|
+
<Checkbox
|
|
690
|
+
checked={this.state.showSeparators}
|
|
691
|
+
label="Show separators"
|
|
692
|
+
variant="toggle"
|
|
693
|
+
onChange={
|
|
694
|
+
() => {
|
|
695
|
+
this.setState({ showSeparators: !this.state.showSeparators })
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
/>
|
|
699
|
+
<Checkbox
|
|
700
|
+
checked={this.state.showTitles}
|
|
701
|
+
label="Show group titles"
|
|
702
|
+
variant="toggle"
|
|
703
|
+
onChange={
|
|
704
|
+
() => {
|
|
705
|
+
this.setState({ showTitles: !this.state.showTitles })
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
/>
|
|
709
|
+
</FormFieldGroup>
|
|
710
|
+
</Flex.Item>
|
|
711
|
+
</Flex>
|
|
712
|
+
)
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
render(<GroupsExample />)
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
#### Selectable Groups
|
|
720
|
+
|
|
721
|
+
The `selectableType` prop makes a group either a single-select (radio) or multi-select (checkbox) group. Selected options are indicated with a "check" icon.
|
|
722
|
+
|
|
723
|
+
It is recommended to set the `value` prop of the options in a group, because the `defaultSelected` and `onSelect` props are based on these values.
|
|
724
|
+
|
|
725
|
+
##### Single-select Group
|
|
726
|
+
|
|
727
|
+
```js
|
|
728
|
+
---
|
|
729
|
+
example: true
|
|
730
|
+
---
|
|
731
|
+
<Drilldown rootPageId='root' width="20rem" maxHeight="30rem">
|
|
732
|
+
<Drilldown.Page id="root">
|
|
733
|
+
<Drilldown.Group
|
|
734
|
+
id="group1"
|
|
735
|
+
renderGroupTitle="Select one option"
|
|
736
|
+
selectableType='single'
|
|
737
|
+
>
|
|
738
|
+
<Drilldown.Option id="+2" value="+2" defaultSelected>
|
|
739
|
+
Strongly agree
|
|
740
|
+
</Drilldown.Option>
|
|
741
|
+
<Drilldown.Option id="+1" value="+1">
|
|
742
|
+
Somewhat agree
|
|
743
|
+
</Drilldown.Option>
|
|
744
|
+
<Drilldown.Option id="0" value="0">
|
|
745
|
+
Neither agree nor disagree
|
|
746
|
+
</Drilldown.Option>
|
|
747
|
+
<Drilldown.Option id="-1" value="-1">
|
|
748
|
+
Somewhat disagree
|
|
749
|
+
</Drilldown.Option>
|
|
750
|
+
<Drilldown.Option id="-2" value="-2">
|
|
751
|
+
Strongly disagree
|
|
752
|
+
</Drilldown.Option>
|
|
753
|
+
</Drilldown.Group>
|
|
754
|
+
</Drilldown.Page>
|
|
755
|
+
</Drilldown>
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
##### Multi-select Group
|
|
759
|
+
|
|
760
|
+
```js
|
|
761
|
+
---
|
|
762
|
+
example: true
|
|
763
|
+
render: false
|
|
764
|
+
---
|
|
765
|
+
|
|
766
|
+
class SelectMembersExample extends React.Component {
|
|
767
|
+
state = {
|
|
768
|
+
selected: {}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
selectMembers(groupValues) {
|
|
772
|
+
this.setState(({ selected }) => {
|
|
773
|
+
return { selected: { ...selected, ...groupValues }}
|
|
774
|
+
})
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
renderGroups(groups) {
|
|
778
|
+
return groups.map((group, idx) => {
|
|
779
|
+
const { id, label, members, selectableType } = group
|
|
780
|
+
|
|
781
|
+
return (
|
|
782
|
+
<Drilldown.Group
|
|
783
|
+
id={id}
|
|
784
|
+
key={idx}
|
|
785
|
+
renderGroupTitle={label}
|
|
786
|
+
selectableType={selectableType}
|
|
787
|
+
onSelect={(_e, { value }) => {
|
|
788
|
+
this.selectMembers({ [id]: value })
|
|
789
|
+
}}
|
|
790
|
+
>
|
|
791
|
+
{members.map((member, idx) => {
|
|
792
|
+
const { id, name } = member
|
|
793
|
+
return (
|
|
794
|
+
<Drilldown.Option key={idx} id={id} value={name}>
|
|
795
|
+
{name}
|
|
796
|
+
</Drilldown.Option>
|
|
797
|
+
)
|
|
798
|
+
})}
|
|
799
|
+
|
|
800
|
+
</Drilldown.Group>
|
|
801
|
+
)
|
|
802
|
+
})
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
renderSubPageOptions(subPages = []) {
|
|
806
|
+
return subPages.map((subPage, idx) => {
|
|
807
|
+
const { id, label } = subPage
|
|
808
|
+
|
|
809
|
+
return (
|
|
810
|
+
<Drilldown.Option key={idx} id={id} subPageId={id}>
|
|
811
|
+
{label}
|
|
812
|
+
</Drilldown.Option>
|
|
813
|
+
)
|
|
814
|
+
})
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
renderPage(category, key) {
|
|
818
|
+
const { id, subPages = [], groups = [] } = category
|
|
819
|
+
|
|
820
|
+
return (
|
|
821
|
+
<Drilldown.Page key={key} id={id}>
|
|
822
|
+
{subPages.length > 0 && this.renderSubPageOptions(subPages)}
|
|
823
|
+
{groups.length > 0 && this.renderGroups(groups)}
|
|
824
|
+
</Drilldown.Page>
|
|
825
|
+
)
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
render() {
|
|
829
|
+
const { pages } = this.props
|
|
830
|
+
const { selected = {} } = this.state
|
|
831
|
+
|
|
832
|
+
return (
|
|
833
|
+
<Flex alignItems='start'>
|
|
834
|
+
<Flex.Item width="20rem" textAlign="center" padding='x-small 0 0'>
|
|
835
|
+
<Drilldown
|
|
836
|
+
rootPageId="root"
|
|
837
|
+
width="18.5rem"
|
|
838
|
+
maxHeight="26.5rem"
|
|
839
|
+
trigger={<Button>Select Members</Button>}
|
|
840
|
+
shouldHideOnSelect={false}
|
|
841
|
+
onToggle={(_event, { shown }) => {
|
|
842
|
+
shown && this.selectMembers({})
|
|
843
|
+
}}
|
|
844
|
+
>
|
|
845
|
+
{pages.map((page, idx) => this.renderPage(page, idx))}
|
|
846
|
+
</Drilldown>
|
|
847
|
+
</Flex.Item>
|
|
848
|
+
|
|
849
|
+
<Flex.Item padding='0 0 0 large' shouldGrow shouldShrink>
|
|
850
|
+
<View as="div" padding='small' background='primary'>
|
|
851
|
+
<div>
|
|
852
|
+
<Text weight='bold'>Selected members:</Text>
|
|
853
|
+
</div>
|
|
854
|
+
<div>
|
|
855
|
+
<List>
|
|
856
|
+
{Object.entries(selected).map(([ groupId, values ], idx) => {
|
|
857
|
+
return values.length > 0 ? (
|
|
858
|
+
<List.Item key={idx}>
|
|
859
|
+
<b>{groupId}</b>: {values.join(', ')}
|
|
860
|
+
</List.Item>
|
|
861
|
+
) : null
|
|
862
|
+
})}
|
|
863
|
+
</List>
|
|
864
|
+
</div>
|
|
865
|
+
</View>
|
|
866
|
+
</Flex.Item>
|
|
867
|
+
</Flex>
|
|
868
|
+
)
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
const pages = [
|
|
873
|
+
{
|
|
874
|
+
id: 'root',
|
|
875
|
+
subPages: [
|
|
876
|
+
{ id: 'courses', label: 'Courses' },
|
|
877
|
+
{ id: 'groups', label: 'Groups' },
|
|
878
|
+
{ id: 'consortiums', label: 'Consortiums' }
|
|
879
|
+
]
|
|
880
|
+
},
|
|
881
|
+
{
|
|
882
|
+
id: 'courses',
|
|
883
|
+
groups: [
|
|
884
|
+
{
|
|
885
|
+
id: 'course1',
|
|
886
|
+
label: 'Course 1',
|
|
887
|
+
selectableType: 'multiple',
|
|
888
|
+
members: [
|
|
889
|
+
{ id: 'course1_1', name: 'Hanna Septimus' },
|
|
890
|
+
{ id: 'course1_2', name: 'Kadin Press' },
|
|
891
|
+
{ id: 'course1_3', name: 'Kaiya Botosh' },
|
|
892
|
+
]
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
id: 'course2',
|
|
896
|
+
label: 'Course 2',
|
|
897
|
+
selectableType: 'multiple',
|
|
898
|
+
members: [
|
|
899
|
+
{ id: 'course2_1', name: 'Leo Calzoni' },
|
|
900
|
+
{ id: 'course2_2', name: 'Gretchen Gouse' },
|
|
901
|
+
]
|
|
902
|
+
}
|
|
903
|
+
]
|
|
904
|
+
},
|
|
905
|
+
{
|
|
906
|
+
id: 'groups',
|
|
907
|
+
groups: [
|
|
908
|
+
{
|
|
909
|
+
id: 'group1',
|
|
910
|
+
label: 'Group 1',
|
|
911
|
+
selectableType: 'multiple',
|
|
912
|
+
members: [
|
|
913
|
+
{ id: 'groups1_1', name: 'Penka Okabe' },
|
|
914
|
+
{ id: 'groups1_2', name: 'Ausma Meggyesfalvi' },
|
|
915
|
+
{ id: 'groups1_3', name: 'Endrit Höfler' },
|
|
916
|
+
{ id: 'groups1_4', name: 'Ryuu Carey' },
|
|
917
|
+
{ id: 'groups1_5', name: 'Daphne Dioli' },
|
|
918
|
+
]
|
|
919
|
+
}
|
|
920
|
+
]
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
id: 'consortiums',
|
|
924
|
+
groups: [
|
|
925
|
+
{
|
|
926
|
+
id: 'consortium1',
|
|
927
|
+
label: 'Consortium 1',
|
|
928
|
+
selectableType: 'multiple',
|
|
929
|
+
members: [
|
|
930
|
+
{ id: 'consortium1_1', name: 'Brahim Gustavsson' },
|
|
931
|
+
{ id: 'consortium1_2', name: 'Elodia Dreschner' },
|
|
932
|
+
]
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
id: 'consortium2',
|
|
936
|
+
label: 'Consortium 2',
|
|
937
|
+
selectableType: 'multiple',
|
|
938
|
+
members: [
|
|
939
|
+
{ id: 'consortium2_1', name: 'Darwin Peter' },
|
|
940
|
+
{ id: 'consortium2_2', name: 'Sukhrab Burnham' },
|
|
941
|
+
]
|
|
942
|
+
},
|
|
943
|
+
{
|
|
944
|
+
id: 'consortium3',
|
|
945
|
+
label: 'Consortium 3',
|
|
946
|
+
selectableType: 'multiple',
|
|
947
|
+
members: [
|
|
948
|
+
{ id: 'consortium3_1', name: 'Jeffry Antonise' },
|
|
949
|
+
{ id: 'consortium3_2', name: 'Bia Regenbogen' },
|
|
950
|
+
]
|
|
951
|
+
}
|
|
952
|
+
]
|
|
953
|
+
}
|
|
954
|
+
]
|
|
955
|
+
|
|
956
|
+
render(<SelectMembersExample pages={pages} />)
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
### Disabled prop
|
|
960
|
+
|
|
961
|
+
You can disable the whole Drilldown or its sub-components with the `disabled` prop. The only option that can not be disabled is the Back navigation.
|
|
962
|
+
|
|
963
|
+
```js
|
|
964
|
+
---
|
|
965
|
+
example: true
|
|
966
|
+
render: false
|
|
967
|
+
---
|
|
968
|
+
class DisabledExample extends React.Component {
|
|
969
|
+
state = {
|
|
970
|
+
disabled: 'None',
|
|
971
|
+
withTrigger: false
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
render() {
|
|
975
|
+
const { disabled, withTrigger } = this.state
|
|
976
|
+
|
|
977
|
+
const disabledDrilldown = disabled === 'Drilldown'
|
|
978
|
+
const disabledPages = disabled === 'Pages'
|
|
979
|
+
const disabledGroups = disabled === 'Groups'
|
|
980
|
+
const disabledOptions = disabled === 'Options'
|
|
981
|
+
|
|
982
|
+
return (
|
|
983
|
+
<Flex alignItems='start'>
|
|
984
|
+
<Flex.Item width="20rem" textAlign='center' padding='x-small 0 0'>
|
|
985
|
+
<View textAlign='start'>
|
|
986
|
+
<Drilldown
|
|
987
|
+
rootPageId='root'
|
|
988
|
+
width="20rem"
|
|
989
|
+
maxHeight="30rem"
|
|
990
|
+
trigger={withTrigger && <Button>Toggle button</Button>}
|
|
991
|
+
disabled={disabledDrilldown}
|
|
992
|
+
>
|
|
993
|
+
<Drilldown.Page
|
|
994
|
+
id="root"
|
|
995
|
+
renderTitle="Root page"
|
|
996
|
+
renderActionLabel="Action"
|
|
997
|
+
disabled={disabledPages}
|
|
998
|
+
>
|
|
999
|
+
<Drilldown.Option
|
|
1000
|
+
id="option1"
|
|
1001
|
+
subPageId='secondPage'
|
|
1002
|
+
disabled={disabledOptions}
|
|
1003
|
+
>
|
|
1004
|
+
Option with subPage navigation
|
|
1005
|
+
</Drilldown.Option>
|
|
1006
|
+
<Drilldown.Option id="option2" disabled={disabledOptions}>
|
|
1007
|
+
Option
|
|
1008
|
+
</Drilldown.Option>
|
|
1009
|
+
<Drilldown.Group
|
|
1010
|
+
id="group1"
|
|
1011
|
+
renderGroupTitle="Selectable Group"
|
|
1012
|
+
selectableType='multiple'
|
|
1013
|
+
disabled={disabledGroups}
|
|
1014
|
+
>
|
|
1015
|
+
{['Apple', 'Orange', 'Banana', 'Strawberry'].map(
|
|
1016
|
+
item => (
|
|
1017
|
+
<Drilldown.Option
|
|
1018
|
+
id={item}
|
|
1019
|
+
key={item}
|
|
1020
|
+
value={item}
|
|
1021
|
+
defaultSelected={item === 'Apple'}
|
|
1022
|
+
disabled={disabledOptions}
|
|
1023
|
+
>
|
|
1024
|
+
{item}
|
|
1025
|
+
</Drilldown.Option>
|
|
1026
|
+
)
|
|
1027
|
+
)}
|
|
1028
|
+
</Drilldown.Group>
|
|
1029
|
+
</Drilldown.Page>
|
|
1030
|
+
|
|
1031
|
+
<Drilldown.Page
|
|
1032
|
+
id="secondPage"
|
|
1033
|
+
renderTitle="Second page"
|
|
1034
|
+
disabled={disabledPages}
|
|
1035
|
+
>
|
|
1036
|
+
{['Option 1', 'Option 2', 'Option 3', 'Option 4'].map(
|
|
1037
|
+
item => (
|
|
1038
|
+
<Drilldown.Option
|
|
1039
|
+
id={item}
|
|
1040
|
+
key={item}
|
|
1041
|
+
disabled={disabledOptions}
|
|
1042
|
+
>
|
|
1043
|
+
{item}
|
|
1044
|
+
</Drilldown.Option>
|
|
1045
|
+
)
|
|
1046
|
+
)}
|
|
1047
|
+
</Drilldown.Page>
|
|
1048
|
+
</Drilldown>
|
|
1049
|
+
</View>
|
|
1050
|
+
</Flex.Item>
|
|
1051
|
+
|
|
1052
|
+
<Flex.Item padding="0 0 0 large" shouldGrow shouldShrink>
|
|
1053
|
+
<FormFieldGroup
|
|
1054
|
+
description={<ScreenReaderContent>Settings</ScreenReaderContent>}
|
|
1055
|
+
colSpacing="medium"
|
|
1056
|
+
layout="columns"
|
|
1057
|
+
vAlign="top"
|
|
1058
|
+
>
|
|
1059
|
+
<RadioInputGroup
|
|
1060
|
+
onChange={(_event, value) => {
|
|
1061
|
+
this.setState({ disabled: value })
|
|
1062
|
+
}}
|
|
1063
|
+
defaultValue="None"
|
|
1064
|
+
name="disabledPart"
|
|
1065
|
+
description="Select disabled"
|
|
1066
|
+
>
|
|
1067
|
+
{['None', 'Drilldown', 'Pages', 'Groups', 'Options'].map(part => <RadioInput key={part} value={part} label={part} />)}
|
|
1068
|
+
</RadioInputGroup>
|
|
1069
|
+
|
|
1070
|
+
<Checkbox
|
|
1071
|
+
checked={this.state.withTrigger}
|
|
1072
|
+
label="With toggle button"
|
|
1073
|
+
variant="toggle"
|
|
1074
|
+
onChange={
|
|
1075
|
+
() => {
|
|
1076
|
+
this.setState({ withTrigger: !this.state.withTrigger })
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
/>
|
|
1080
|
+
</FormFieldGroup>
|
|
1081
|
+
</Flex.Item>
|
|
1082
|
+
</Flex>
|
|
1083
|
+
)
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
render(<DisabledExample />)
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
### shouldHideOnSelect
|
|
1091
|
+
|
|
1092
|
+
By default, if the Drilldown is in a Popover, it will hide if an option is selected (except on page navigation). You can disable this behavior by setting `shouldHideOnSelect={false}`.
|
|
1093
|
+
|
|
1094
|
+
You can still manually close the Popover on option click or select, calling the `.hide()` method on the `drilldown` parameter of the `onOptionClick` and `onSelect` callbacks.
|
|
1095
|
+
|
|
1096
|
+
```js
|
|
1097
|
+
---
|
|
1098
|
+
example: true
|
|
1099
|
+
---
|
|
1100
|
+
<Drilldown
|
|
1101
|
+
rootPageId='root'
|
|
1102
|
+
width="20rem"
|
|
1103
|
+
maxHeight="30rem"
|
|
1104
|
+
trigger={<Button>Toggle button</Button>}
|
|
1105
|
+
shouldHideOnSelect={false}
|
|
1106
|
+
>
|
|
1107
|
+
<Drilldown.Page id="root" renderTitle='Should not hide on select'>
|
|
1108
|
+
<Drilldown.Option id="option1">
|
|
1109
|
+
Option
|
|
1110
|
+
</Drilldown.Option>
|
|
1111
|
+
<Drilldown.Option id="option2">
|
|
1112
|
+
Option
|
|
1113
|
+
</Drilldown.Option>
|
|
1114
|
+
<Drilldown.Option id="option3">
|
|
1115
|
+
Option
|
|
1116
|
+
</Drilldown.Option>
|
|
1117
|
+
<Drilldown.Option
|
|
1118
|
+
id="close"
|
|
1119
|
+
renderBeforeLabel={<IconXLine />}
|
|
1120
|
+
onOptionClick={(_event, data) => {
|
|
1121
|
+
data.drilldown.hide()
|
|
1122
|
+
}}
|
|
1123
|
+
themeOverride={(_componentTheme, currentTheme) => {
|
|
1124
|
+
return { color: currentTheme.colors.textDanger }
|
|
1125
|
+
}}
|
|
1126
|
+
>
|
|
1127
|
+
Close Popover
|
|
1128
|
+
</Drilldown.Option>
|
|
1129
|
+
</Drilldown.Page>
|
|
1130
|
+
</Drilldown>
|
|
1131
|
+
```
|
|
1132
|
+
|
|
1133
|
+
### Page navigation
|
|
1134
|
+
|
|
1135
|
+
The recommended way to navigate between pages is to add the `subaPageId` prop to the `<Drilldown.Option>`-s. This will make an arrow display on the option to indicate that it navigates to another page.
|
|
1136
|
+
|
|
1137
|
+
If you have to manually navigate to another page on click or on Popover toggle, the `onOptionClick` and `onToggle` callbacks expose methods for page navigation and the current page history.
|
|
1138
|
+
|
|
1139
|
+
- `pageHistory`: An array of the Page Id-s in "breadcrumbs" fashion.
|
|
1140
|
+
- `goToPreviousPage`: A method that navigates to the previous page (if not on root page).
|
|
1141
|
+
- `goToPage`: A method that navigates to a page, defined by the page id. If that page is already in the page history, it goes back to that level. If it is not in the history and the page exists, it adds it to the page history and goes there.
|
|
1142
|
+
|
|
1143
|
+
These two navigation methods are also available as public methods on the Drilldown component.
|
|
1144
|
+
|
|
1145
|
+
```js
|
|
1146
|
+
---
|
|
1147
|
+
example: true
|
|
1148
|
+
---
|
|
1149
|
+
<Drilldown
|
|
1150
|
+
rootPageId='Root Page'
|
|
1151
|
+
width="20rem"
|
|
1152
|
+
maxHeight="30rem"
|
|
1153
|
+
>
|
|
1154
|
+
<Drilldown.Page id="Root Page" renderTitle='Root page'>
|
|
1155
|
+
<Drilldown.Option id="option1" subPageId='Page Two'>
|
|
1156
|
+
Go to Page Two
|
|
1157
|
+
</Drilldown.Option>
|
|
1158
|
+
<Drilldown.Option id="option2" subPageId='Page Three'>
|
|
1159
|
+
Go to Page Three
|
|
1160
|
+
</Drilldown.Option>
|
|
1161
|
+
<Drilldown.Option
|
|
1162
|
+
id="option3"
|
|
1163
|
+
onOptionClick={(_event, args) => {
|
|
1164
|
+
console.log(args.pageHistory)
|
|
1165
|
+
const nav = args.goToPage('Page Two')
|
|
1166
|
+
console.log(`Navigated from "${nav.prevPageId}" to "${nav.newPageId}"`)
|
|
1167
|
+
}}
|
|
1168
|
+
description='Navigates to Page Two on click and logs the pageHistory on the console.'
|
|
1169
|
+
>
|
|
1170
|
+
Manual navigation to Page Two
|
|
1171
|
+
</Drilldown.Option>
|
|
1172
|
+
</Drilldown.Page>
|
|
1173
|
+
|
|
1174
|
+
<Drilldown.Page id="Page Two" renderTitle='Page Two'>
|
|
1175
|
+
<Drilldown.Option id="option1" subPageId='Page Three'>
|
|
1176
|
+
Go to Page Three
|
|
1177
|
+
</Drilldown.Option>
|
|
1178
|
+
<Drilldown.Option
|
|
1179
|
+
id="option2"
|
|
1180
|
+
onOptionClick={(_event, args) => {
|
|
1181
|
+
console.log(args.pageHistory)
|
|
1182
|
+
const nav = args.goToPreviousPage()
|
|
1183
|
+
console.log(`Navigated back from "${nav.prevPageId}" to previous "${nav.newPageId}"`)
|
|
1184
|
+
}}
|
|
1185
|
+
description='Navigates to the previous page on click and logs the pageHistory on the console.'
|
|
1186
|
+
>
|
|
1187
|
+
Manual "Back" navigation
|
|
1188
|
+
</Drilldown.Option>
|
|
1189
|
+
<Drilldown.Option
|
|
1190
|
+
id="option3"
|
|
1191
|
+
onOptionClick={(_event, args) => {
|
|
1192
|
+
console.log(args.pageHistory)
|
|
1193
|
+
const nav = args.goToPage('Page Three')
|
|
1194
|
+
console.log(`Navigated from "${nav.prevPageId}" to "${nav.newPageId}"`)
|
|
1195
|
+
}}
|
|
1196
|
+
description='Navigates to Page Two on click and logs the pageHistory on the console.'
|
|
1197
|
+
>
|
|
1198
|
+
Manual navigation to Page Three
|
|
1199
|
+
</Drilldown.Option>
|
|
1200
|
+
</Drilldown.Page>
|
|
1201
|
+
|
|
1202
|
+
<Drilldown.Page id="Page Three" renderTitle='Page Three'>
|
|
1203
|
+
<Drilldown.Option
|
|
1204
|
+
id="option1"
|
|
1205
|
+
onOptionClick={(_event, args) => {
|
|
1206
|
+
console.log(args.pageHistory)
|
|
1207
|
+
const nav = args.goToPage(args.pageHistory[0])
|
|
1208
|
+
console.log(`Navigated from "${nav.prevPageId}" to "${nav.newPageId}"`)
|
|
1209
|
+
}}
|
|
1210
|
+
description='Navigates to the first page on click and logs the pageHistory on the console.'
|
|
1211
|
+
>
|
|
1212
|
+
Manual "Back" navigation
|
|
1213
|
+
</Drilldown.Option>
|
|
1214
|
+
<Drilldown.Option
|
|
1215
|
+
id="option2"
|
|
1216
|
+
onOptionClick={(_event, args) => {
|
|
1217
|
+
console.log(args.pageHistory)
|
|
1218
|
+
const nav = args.goToPreviousPage()
|
|
1219
|
+
console.log(`Navigated back from "${nav.prevPageId}" to previous "${nav.newPageId}"`)
|
|
1220
|
+
}}
|
|
1221
|
+
description='Navigates to the previous page on click and logs the pageHistory on the console.'
|
|
1222
|
+
>
|
|
1223
|
+
Manual "Back" navigation
|
|
1224
|
+
</Drilldown.Option>
|
|
1225
|
+
</Drilldown.Page>
|
|
1226
|
+
</Drilldown>
|
|
1227
|
+
```
|
|
1228
|
+
|
|
1229
|
+
### Editable structures
|
|
1230
|
+
|
|
1231
|
+
The following example showcases an editable drilldown that can add or delete options.
|
|
1232
|
+
|
|
1233
|
+
```js
|
|
1234
|
+
---
|
|
1235
|
+
example: true
|
|
1236
|
+
render: false
|
|
1237
|
+
---
|
|
1238
|
+
|
|
1239
|
+
class EditableStructureExample extends React.Component {
|
|
1240
|
+
constructor(props) {
|
|
1241
|
+
super(props)
|
|
1242
|
+
|
|
1243
|
+
this.state = {
|
|
1244
|
+
districtsData: {
|
|
1245
|
+
d1: {
|
|
1246
|
+
label: 'District 1',
|
|
1247
|
+
schools: ['s1']
|
|
1248
|
+
}
|
|
1249
|
+
},
|
|
1250
|
+
schoolsData: {
|
|
1251
|
+
s1: {
|
|
1252
|
+
label: 'School 1',
|
|
1253
|
+
districtId: 'd1',
|
|
1254
|
+
isSelected: false
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
this.districtCounter = Object.keys(this.state.districtsData).length
|
|
1260
|
+
this.schoolCounter = Object.keys(this.state.schoolsData).length
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
toggleSelectedSchool(id, school) {
|
|
1264
|
+
this.setState({ schoolsData: {
|
|
1265
|
+
...this.state.schoolsData,
|
|
1266
|
+
[id]: { ...school, isSelected: !school.isSelected }
|
|
1267
|
+
} })
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
renderSelectedIcon(isSelected) {
|
|
1271
|
+
return (
|
|
1272
|
+
<IconCheckSolid
|
|
1273
|
+
style={{ opacity: isSelected ? 1 : 0 }}
|
|
1274
|
+
/>
|
|
1275
|
+
)
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
renderAddAction(label) {
|
|
1279
|
+
return (
|
|
1280
|
+
<span>
|
|
1281
|
+
<IconAddSolid />
|
|
1282
|
+
<View as="span" margin="0 0 0 x-small">New {label}</View>
|
|
1283
|
+
</span>
|
|
1284
|
+
)
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
addDistrict() {
|
|
1288
|
+
const { districtsData } = this.state
|
|
1289
|
+
this.districtCounter++
|
|
1290
|
+
const districtNumber = this.districtCounter
|
|
1291
|
+
const districtId = `d${districtNumber}`
|
|
1292
|
+
|
|
1293
|
+
this.setState({
|
|
1294
|
+
districtsData: {
|
|
1295
|
+
...districtsData,
|
|
1296
|
+
[districtId]: {
|
|
1297
|
+
label: `District ${districtNumber}`,
|
|
1298
|
+
schools: []
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
})
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
addSchool(districtId) {
|
|
1305
|
+
const { districtsData, schoolsData } = this.state
|
|
1306
|
+
const district = districtsData[districtId]
|
|
1307
|
+
|
|
1308
|
+
this.schoolCounter++
|
|
1309
|
+
const schoolNumber = this.schoolCounter
|
|
1310
|
+
const schoolId = `s${schoolNumber}`
|
|
1311
|
+
|
|
1312
|
+
this.setState({
|
|
1313
|
+
districtsData: {
|
|
1314
|
+
...districtsData,
|
|
1315
|
+
[districtId]: {
|
|
1316
|
+
...district,
|
|
1317
|
+
schools: [...district.schools, schoolId]
|
|
1318
|
+
}
|
|
1319
|
+
},
|
|
1320
|
+
schoolsData: {
|
|
1321
|
+
...schoolsData,
|
|
1322
|
+
[schoolId]: {
|
|
1323
|
+
label: `School ${schoolNumber}`,
|
|
1324
|
+
districtId,
|
|
1325
|
+
isSelected: false
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
})
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
renderDeleteOption(type, label, idToDelete) {
|
|
1332
|
+
const id = type === 'school' ? "deleteSchool" : "deleteDistrict"
|
|
1333
|
+
const callback = type === 'school' ? this.deleteSchool : this.deleteDistrict
|
|
1334
|
+
const separatorId = `${idToDelete}__separator`
|
|
1335
|
+
|
|
1336
|
+
return [
|
|
1337
|
+
<Drilldown.Separator id={separatorId} key={separatorId} />,
|
|
1338
|
+
<Drilldown.Option
|
|
1339
|
+
id={id}
|
|
1340
|
+
key={`${idToDelete}__${id}`}
|
|
1341
|
+
onOptionClick={() => { callback(idToDelete) }}
|
|
1342
|
+
themeOverride={(_componentTheme, currentTheme) => {
|
|
1343
|
+
return { color: currentTheme.colors.textDanger }
|
|
1344
|
+
}}
|
|
1345
|
+
>
|
|
1346
|
+
<IconTrashLine />
|
|
1347
|
+
<View as='span' margin='0 0 0 x-small'>
|
|
1348
|
+
Delete {label}
|
|
1349
|
+
</View>
|
|
1350
|
+
</Drilldown.Option>
|
|
1351
|
+
]
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
deleteSchool = (id) => {
|
|
1355
|
+
const { districtsData, schoolsData } = this.state
|
|
1356
|
+
const { [id]: school, ...restSchools } = schoolsData
|
|
1357
|
+
const { districtId } = school
|
|
1358
|
+
const district = districtsData[districtId]
|
|
1359
|
+
|
|
1360
|
+
this.setState({
|
|
1361
|
+
schoolsData: restSchools,
|
|
1362
|
+
districtsData: {
|
|
1363
|
+
...districtsData,
|
|
1364
|
+
[districtId]: {
|
|
1365
|
+
...district,
|
|
1366
|
+
schools: district.schools.filter(schoolId => schoolId !== id)
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
})
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
deleteDistrict = (id) => {
|
|
1373
|
+
const { districtsData, schoolsData } = this.state
|
|
1374
|
+
const { [id]: district, ...restDistricts } = districtsData
|
|
1375
|
+
|
|
1376
|
+
const filteredSchools = {}
|
|
1377
|
+
|
|
1378
|
+
Object.entries(schoolsData).forEach(([schoolId, school]) => {
|
|
1379
|
+
if (school.districtId !== id) {
|
|
1380
|
+
filteredSchools[schoolId] = school
|
|
1381
|
+
}
|
|
1382
|
+
})
|
|
1383
|
+
|
|
1384
|
+
this.setState({
|
|
1385
|
+
districtsData: restDistricts,
|
|
1386
|
+
schoolsData: filteredSchools
|
|
1387
|
+
})
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
render() {
|
|
1391
|
+
const { districtsData, schoolsData } = this.state
|
|
1392
|
+
const districts = Object.entries(districtsData)
|
|
1393
|
+
const schools = Object.entries(schoolsData)
|
|
1394
|
+
|
|
1395
|
+
return (
|
|
1396
|
+
<Drilldown
|
|
1397
|
+
rootPageId="root"
|
|
1398
|
+
width="18.5rem"
|
|
1399
|
+
maxHeight="26.5rem"
|
|
1400
|
+
>
|
|
1401
|
+
<Drilldown.Page
|
|
1402
|
+
id="root"
|
|
1403
|
+
renderTitle="Districts"
|
|
1404
|
+
renderActionLabel={this.renderAddAction('District')}
|
|
1405
|
+
onHeaderActionClicked={() => {
|
|
1406
|
+
this.addDistrict()
|
|
1407
|
+
}}
|
|
1408
|
+
>
|
|
1409
|
+
{districts.map(([id, district]) => {
|
|
1410
|
+
const { label } = district
|
|
1411
|
+
return (
|
|
1412
|
+
<Drilldown.Option
|
|
1413
|
+
key={`districtOption__${id}`}
|
|
1414
|
+
id={id}
|
|
1415
|
+
subPageId={id}
|
|
1416
|
+
>
|
|
1417
|
+
{label}
|
|
1418
|
+
</Drilldown.Option>
|
|
1419
|
+
)
|
|
1420
|
+
})}
|
|
1421
|
+
</Drilldown.Page>
|
|
1422
|
+
|
|
1423
|
+
{districts.map(([districtId , district]) => {
|
|
1424
|
+
const { label, schools } = district
|
|
1425
|
+
return (
|
|
1426
|
+
<Drilldown.Page
|
|
1427
|
+
key={`districtPage__${districtId}`}
|
|
1428
|
+
id={districtId}
|
|
1429
|
+
renderTitle={label}
|
|
1430
|
+
renderActionLabel={this.renderAddAction('School')}
|
|
1431
|
+
onHeaderActionClicked={() => {
|
|
1432
|
+
this.addSchool(districtId)
|
|
1433
|
+
}}
|
|
1434
|
+
>
|
|
1435
|
+
{schools.map((id) => {
|
|
1436
|
+
const { label, isSelected } = this.state.schoolsData[id]
|
|
1437
|
+
return (
|
|
1438
|
+
<Drilldown.Option
|
|
1439
|
+
key={`schoolOption__${districtId}--${id}`}
|
|
1440
|
+
id={id}
|
|
1441
|
+
subPageId={id}
|
|
1442
|
+
renderBeforeLabel={this.renderSelectedIcon(isSelected)}
|
|
1443
|
+
>
|
|
1444
|
+
{label}
|
|
1445
|
+
</Drilldown.Option>
|
|
1446
|
+
)
|
|
1447
|
+
})}
|
|
1448
|
+
{this.renderDeleteOption('district', label, districtId)}
|
|
1449
|
+
</Drilldown.Page>
|
|
1450
|
+
)
|
|
1451
|
+
})}
|
|
1452
|
+
|
|
1453
|
+
{schools.map(([id, school]) => {
|
|
1454
|
+
const { label, isSelected } = school
|
|
1455
|
+
return (
|
|
1456
|
+
<Drilldown.Page
|
|
1457
|
+
key={`schoolPage__${id}`}
|
|
1458
|
+
id={id}
|
|
1459
|
+
withoutHeaderSeparator
|
|
1460
|
+
>
|
|
1461
|
+
<Drilldown.Option
|
|
1462
|
+
id="toggleSchool"
|
|
1463
|
+
renderBeforeLabel={this.renderSelectedIcon(isSelected)}
|
|
1464
|
+
onOptionClick={() => {
|
|
1465
|
+
this.toggleSelectedSchool(id, school)
|
|
1466
|
+
}}
|
|
1467
|
+
>
|
|
1468
|
+
{isSelected ? 'Deselect' : 'Select'} {label}
|
|
1469
|
+
</Drilldown.Option>
|
|
1470
|
+
{this.renderDeleteOption('school', label, id)}
|
|
1471
|
+
</Drilldown.Page>
|
|
1472
|
+
)
|
|
1473
|
+
})}
|
|
1474
|
+
</Drilldown>
|
|
1475
|
+
)
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
render(<EditableStructureExample />)
|
|
1480
|
+
```
|
|
1481
|
+
|
|
1482
|
+
### Guidelines
|
|
1483
|
+
|
|
1484
|
+
```js
|
|
1485
|
+
---
|
|
1486
|
+
guidelines: true
|
|
1487
|
+
---
|
|
1488
|
+
<Guidelines>
|
|
1489
|
+
<Figure recommendation="yes" title="Do">
|
|
1490
|
+
<Figure.Item>
|
|
1491
|
+
Use to display tree structures (instead of the TreeBrowser)
|
|
1492
|
+
</Figure.Item>
|
|
1493
|
+
<Figure.Item>
|
|
1494
|
+
Use instead of flyout menus
|
|
1495
|
+
</Figure.Item>
|
|
1496
|
+
<Figure.Item>
|
|
1497
|
+
Use for radio or checkbox type interactions
|
|
1498
|
+
</Figure.Item>
|
|
1499
|
+
<Figure.Item>
|
|
1500
|
+
Make the text within Drilldown options direct and self-evident so users can quickly decide on an action
|
|
1501
|
+
</Figure.Item>
|
|
1502
|
+
</Figure>
|
|
1503
|
+
<Figure recommendation="no" title="Don't">
|
|
1504
|
+
<Figure.Item>
|
|
1505
|
+
Include complex content
|
|
1506
|
+
</Figure.Item>
|
|
1507
|
+
<Figure.Item>
|
|
1508
|
+
Use content that is not a Drilldown.Option (eg. buttons)
|
|
1509
|
+
</Figure.Item>
|
|
1510
|
+
<Figure.Item>
|
|
1511
|
+
Use the “back” option to display group names
|
|
1512
|
+
</Figure.Item>
|
|
1513
|
+
</Figure>
|
|
1514
|
+
</Guidelines>
|
|
1515
|
+
```
|