superdesk-ui-framework 3.1.12 → 3.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/app/styles/_helpers.scss +17 -0
  2. package/app/styles/_toggle-box.scss +45 -28
  3. package/app/styles/form-elements/_inputs.scss +14 -0
  4. package/app/styles/grids/_grid-layout.scss +3 -0
  5. package/app-typescript/components/DatePicker.tsx +16 -0
  6. package/app-typescript/components/Layouts/LayoutContainer.tsx +7 -1
  7. package/app-typescript/components/Layouts/PageLayout.tsx +2 -1
  8. package/app-typescript/components/TimePickerV2.tsx +222 -0
  9. package/app-typescript/components/ToggleBox/CustomHeaderToggleBox.tsx +6 -1
  10. package/app-typescript/components/ToggleBox/SimpleToggleBox.tsx +10 -20
  11. package/app-typescript/components/ToggleBox/index.tsx +3 -1
  12. package/app-typescript/components/TreeMenu.tsx +8 -5
  13. package/app-typescript/components/TreeSelect/TreeSelect.tsx +13 -12
  14. package/app-typescript/components/TreeSelect/TreeSelectItem.tsx +11 -1
  15. package/app-typescript/index.ts +1 -0
  16. package/dist/components/TimePicker.tsx +43 -1
  17. package/dist/components/Togglebox.tsx +19 -4
  18. package/dist/components/TreeMenu.tsx +2 -0
  19. package/dist/components/utilities/TextUtilities.tsx +39 -0
  20. package/dist/design-patterns/ThreePaneLayoutPattern.tsx +1 -1
  21. package/dist/examples.bundle.js +2362 -2098
  22. package/dist/superdesk-ui.bundle.css +72 -20
  23. package/dist/superdesk-ui.bundle.js +1986 -1771
  24. package/dist/vendor.bundle.js +16 -16
  25. package/examples/pages/components/TimePicker.tsx +43 -1
  26. package/examples/pages/components/Togglebox.tsx +19 -4
  27. package/examples/pages/components/TreeMenu.tsx +2 -0
  28. package/examples/pages/components/utilities/TextUtilities.tsx +39 -0
  29. package/examples/pages/design-patterns/ThreePaneLayoutPattern.tsx +1 -1
  30. package/package.json +1 -1
  31. package/react/components/DatePicker.d.ts +2 -0
  32. package/react/components/DatePicker.js +6 -2
  33. package/react/components/Layouts/LayoutContainer.d.ts +1 -0
  34. package/react/components/Layouts/LayoutContainer.js +8 -1
  35. package/react/components/Layouts/PageLayout.d.ts +1 -0
  36. package/react/components/Layouts/PageLayout.js +1 -1
  37. package/react/components/TimePickerV2.d.ts +28 -0
  38. package/react/components/TimePickerV2.js +189 -0
  39. package/react/components/ToggleBox/CustomHeaderToggleBox.d.ts +1 -0
  40. package/react/components/ToggleBox/CustomHeaderToggleBox.js +6 -3
  41. package/react/components/ToggleBox/SimpleToggleBox.js +8 -6
  42. package/react/components/ToggleBox/index.d.ts +2 -1
  43. package/react/components/TreeMenu.js +7 -7
  44. package/react/components/TreeSelect/TreeSelect.js +9 -11
  45. package/react/components/TreeSelect/TreeSelectItem.d.ts +1 -0
  46. package/react/components/TreeSelect/TreeSelectItem.js +7 -4
  47. package/react/index.d.ts +1 -0
  48. package/react/index.js +5 -3
@@ -1721,6 +1721,23 @@ h6 {
1721
1721
  word-break: break-all;
1722
1722
  }
1723
1723
 
1724
+ // LINE HIGHT
1725
+ .line-height-1 {
1726
+ line-height: 1;
1727
+ }
1728
+ .line-height-xs {
1729
+ line-height: 1.1;
1730
+ }
1731
+ .line-height-sm {
1732
+ line-height: 1.2;
1733
+ }
1734
+ .line-height-md {
1735
+ line-height: 1.4;
1736
+ }
1737
+ .line-height-lg {
1738
+ line-height: 1.5;
1739
+ }
1740
+
1724
1741
  // LINE CLAMP
1725
1742
  // limit the number of horizontal lines in an elemnt (e.g. <p>)
1726
1743
  // it doesn't work if the element has set flex-grow: 1;
@@ -2,30 +2,31 @@
2
2
 
3
3
  .toggle-box {
4
4
  position: relative;
5
- align-self: stretch;
6
-
5
+ display: grid;
6
+ grid-template-columns: 1fr;
7
+ grid-template-rows: auto 1fr auto;
8
+ transition: grid-template-rows 0.2s ease;
7
9
  &.hidden {
10
+ grid-template-rows: auto 0fr auto;
11
+ transition: grid-template-rows 0.2s ease;
8
12
  .toggle-box__chevron {
9
13
  i {
10
14
  transform: rotate(0);
11
15
  }
12
16
  }
13
-
14
17
  .toggle-box__chevron--circle {
15
18
  background-color: var(--sd-colour-btn-bg-neutral);
16
-
17
19
  i {
18
20
  color: var(--color-text-light);
19
21
  }
20
22
  }
21
-
22
23
  .toggle-box__content-wraper {
23
- max-height: 0;
24
24
  transition: all ease-out .5s;
25
25
  }
26
-
27
- .toggle-box__header {
28
- margin-block-end: 0;
26
+ }
27
+ &.open {
28
+ .toggle-box__content {
29
+ animation: fadeIn 0.3s ease-in 0s 1;
29
30
  }
30
31
  }
31
32
 
@@ -39,23 +40,35 @@
39
40
  }
40
41
  }
41
42
  }
43
+ &.toggle-box--large-title {
44
+ .toggle-box__header,
45
+ a.toggle-box__header {
46
+ .toggle-box__label {
47
+ font-size: var(--text-size-medium);
48
+ color: var(--color-text);
49
+ text-transform: none;
50
+ font-weight: 500;
51
+ }
52
+ &:hover {
53
+ .toggle-box__label {
54
+ color: var(--sd-colour-interactive);
55
+ }
56
+ }
57
+ }
58
+ }
42
59
 
43
60
  &.toggle-box--marg-b10 {
44
61
  margin-block-end: 1rem;
45
62
  }
46
-
47
63
  &.toggle-box--margin-none {
48
64
  margin-block-end: 0;
49
65
  }
50
-
51
66
  &.toggle-box--margin-small {
52
67
  margin-block-end: $sd-base-increment * 2;
53
68
  }
54
-
55
69
  &.toggle-box--margin-normal {
56
70
  margin-block-end: $sd-base-increment * 3;
57
71
  }
58
-
59
72
  &.toggle-box--margin-large {
60
73
  margin-block-end: $sd-base-increment * 4;
61
74
  }
@@ -73,12 +86,12 @@
73
86
 
74
87
  .toggle-box__header,
75
88
  a.toggle-box__header {
89
+ grid-row: 1 / 2;
76
90
  display: flex;
77
91
  cursor: pointer;
78
- margin-block-end: 1.8rem;
79
92
  align-items: center;
80
93
  text-decoration: none !important;
81
-
94
+ align-self: flex-start;
82
95
  .toggle-box__chevron {
83
96
  i {
84
97
  transition: all .3s;
@@ -87,10 +100,11 @@ a.toggle-box__header {
87
100
  }
88
101
 
89
102
  .toggle-box__label {
90
- font-size: 1.2rem;
91
- padding: 0 1rem 0 0.8rem;
92
- color: var(--color-label-text);
103
+ font-size: var(--text-size-x-small);
104
+ padding: 0 var(--space--2) 0 var(--space--1);
105
+ color: var(--color-text-light);
93
106
  text-transform: uppercase;
107
+ transition: color 0.2s ease;
94
108
  }
95
109
 
96
110
  .toggle-box__line {
@@ -101,7 +115,8 @@ a.toggle-box__header {
101
115
  }
102
116
 
103
117
  &:hover {
104
- .toggle-box__label {
118
+ .toggle-box__label,
119
+ {
105
120
  color: var(--sd-colour-interactive);
106
121
  text-decoration: none !important;
107
122
  }
@@ -115,7 +130,6 @@ a.toggle-box__header {
115
130
  .toggle-box__chevron {
116
131
  box-shadow: 0 0 0 3px $sd-colour--focus-shadow;
117
132
  }
118
-
119
133
  .toggle-box__label {
120
134
  color: var(--sd-colour-interactive);
121
135
  }
@@ -127,30 +141,33 @@ a.toggle-box__header {
127
141
  }
128
142
 
129
143
  .toggle-box__content-wraper {
130
- transition: all ease-out .5s;
144
+ grid-row: 2 / 3;
145
+ transition: grid-template-rows 0.2s ease;
146
+ display: flex;
147
+ flex-direction: column;
148
+ justify-content: flex-start;
149
+ align-items: stretch;
150
+ overflow: hidden;
131
151
  }
132
152
 
133
- .toggle-box__content {
134
- animation: fadeIn 0.3s ease-in 0s 1;
153
+ .toggle-box__content {
154
+ overflow-y: auto;
155
+ padding-block-start: 1.8rem;
156
+ flex:1;
135
157
  }
136
158
 
137
159
  // Buttons
138
160
  .toggle-box__button {
139
161
  color: $grayLight;
140
162
  cursor: pointer;
141
-
142
163
  i {
143
164
  color: $grayLight;
144
165
  margin-inline-end: 0.5rem;
145
166
  vertical-align: text-bottom;
146
167
  }
147
-
148
168
  &:hover {
149
169
  text-decoration: none;
150
170
  color: $white;
151
171
  i { color: $white; }
152
172
  }
153
173
  }
154
-
155
-
156
-
@@ -1365,3 +1365,17 @@
1365
1365
  opacity: 0;
1366
1366
  }
1367
1367
  }
1368
+
1369
+ .sd__input__time-picker-v2 {
1370
+ display: flex;
1371
+ }
1372
+
1373
+ .input-wrapper__time-picker-v2 {
1374
+ display: flex;
1375
+ align-items: center;
1376
+ }
1377
+
1378
+ .time-picker-v2-suffix {
1379
+ padding: 0 8px;
1380
+ font-size: 1.6rem;
1381
+ }
@@ -700,6 +700,9 @@ $planningEditor-width: 53rem;
700
700
  grid-template-columns: [slideInLeft] auto [mainContent] 1fr [slideInRight] auto [overlayContainer] 0;
701
701
  grid-template-rows: [headerToolbar] auto [contentBlock] 1fr;
702
702
  overflow: auto;
703
+ &.sd-main-content-grid--full-height {
704
+ height: 100%;
705
+ }
703
706
  }
704
707
 
705
708
  .sd-main-content-grid__header {
@@ -7,6 +7,7 @@ import { throttle } from 'lodash';
7
7
  import nextId from "react-id-generator";
8
8
  import { InputWrapper } from './Form';
9
9
  import { IInputWrapper } from './Form/InputWrapper';
10
+ import {Button} from './Button';
10
11
 
11
12
  export type DatePickerLocaleSettings = Omit<LocaleSettings, 'today' | 'clear'>;
12
13
 
@@ -37,6 +38,8 @@ interface IDatePickerBase extends IInputWrapper {
37
38
  interface IDatePicker extends IDatePickerBase {
38
39
  value: Date | null;
39
40
  onChange(valueNext: Date | null): void;
41
+ maxDate?: Date;
42
+ minDate?: Date;
40
43
  'data-test-id'?: string;
41
44
  }
42
45
 
@@ -157,6 +160,17 @@ export class DatePicker extends React.PureComponent<IDatePicker, IState> {
157
160
  tabindex={this.props.tabindex}
158
161
  >
159
162
  <Calendar
163
+ footerTemplate={this.props.required !== true ? () => (
164
+ <div className='d-flex justify-end'>
165
+ <Button
166
+ onClick={() => {
167
+ this.props.onChange(null);
168
+ }}
169
+ text='Clear'
170
+ data-test-id='clear-button'
171
+ />
172
+ </div>
173
+ ) : undefined}
160
174
  inputId={this.htmlId}
161
175
  ariaLabelledBy={this.htmlId + 'label'}
162
176
  ref={(ref) => {
@@ -208,6 +222,8 @@ export class DatePicker extends React.PureComponent<IDatePicker, IState> {
208
222
  )}
209
223
  appendTo={document.body} // making it work inside `overflow:hidden`
210
224
  disabled={this.props.disabled}
225
+ minDate={this.props.minDate}
226
+ maxDate={this.props.maxDate}
211
227
  onBlur={(event) => {
212
228
  // @ts-ignore: Object is possibly 'null'.
213
229
  if (!event?.target.value) {
@@ -1,13 +1,19 @@
1
1
  import * as React from 'react';
2
+ import classNames from 'classnames';
2
3
 
3
4
  interface IProps {
4
5
  children?: React.ReactNode;
6
+ fullHeight?: boolean;
5
7
  }
6
8
 
7
9
  export class LayoutContainer extends React.PureComponent<IProps> {
8
10
  render() {
11
+ const classes = classNames('sd-content-wrapper__main-content-area sd-main-content-grid comfort', {
12
+ 'sd-main-content-grid--full-height': this.props.fullHeight,
13
+ },
14
+ );
9
15
  return (
10
- <div className='sd-content-wrapper__main-content-area sd-main-content-grid comfort'>
16
+ <div className={classes}>
11
17
  {this.props.children}
12
18
  </div>
13
19
  );
@@ -19,12 +19,13 @@ interface IProps {
19
19
  rightPanelOpen?: boolean;
20
20
  leftPanel?: React.ReactNode;
21
21
  leftPanelOpen?: boolean;
22
+ fullHeight?: boolean; // For cases where the parent container is not display: grid;
22
23
  }
23
24
 
24
25
  export class PageLayout extends React.PureComponent<IProps> {
25
26
  render() {
26
27
  return (
27
- <LayoutContainer>
28
+ <LayoutContainer fullHeight={this.props.fullHeight}>
28
29
  {this.props.header && (
29
30
  <HeaderPanel>
30
31
  {this.props.header}
@@ -0,0 +1,222 @@
1
+ import * as React from 'react';
2
+ import { InputWrapper } from './Form';
3
+ import {IInputWrapper} from './Form/InputWrapper';
4
+ import {padStart, range} from 'lodash';
5
+
6
+ interface IProps extends IInputWrapper {
7
+ value: string; // ISO 8601, 13:59:01
8
+ allowSeconds?: boolean;
9
+ disabledOptions: {
10
+ hours?: Array<number>;
11
+ minutes?: Array<number>;
12
+ seconds?: Array<number>;
13
+ };
14
+ 'data-test-id'?: string;
15
+ onChange(valueNext: string): void;
16
+ }
17
+
18
+ type ITimeUnit = 'hours' | 'minutes' | 'seconds';
19
+
20
+ export class TimePickerV2 extends React.PureComponent<IProps> {
21
+ private is12HourFormat: boolean;
22
+
23
+ constructor(props: IProps) {
24
+ super(props);
25
+
26
+ this.handleTimeChange = this.handleTimeChange.bind(this);
27
+ this.getCorrectedTime = this.getCorrectedTime.bind(this);
28
+ this.getOptionsForTimeUnit = this.getOptionsForTimeUnit.bind(this);
29
+ this.padValue = this.padValue.bind(this);
30
+
31
+ const hour = new Date().toLocaleTimeString([], { hour: 'numeric' });
32
+ this.is12HourFormat = hour.includes('AM') || hour.includes('PM');
33
+ }
34
+
35
+ /**
36
+ * in case initial time is not valid according to disabled options, we return first valid option
37
+ */
38
+ private getCorrectedTime(timeUnit: ITimeUnit, timeStringArray: Array<string>): string {
39
+ const dividedValue = this.props.value.split(':');
40
+ const value = (() => {
41
+ if (timeUnit === 'hours') {
42
+ return dividedValue[0];
43
+ } else if (timeUnit === 'minutes') {
44
+ return dividedValue[1];
45
+ }
46
+
47
+ return dividedValue[2];
48
+ })();
49
+
50
+ if (!(this.props.disabledOptions[timeUnit] ?? []).includes(parseInt(value, 10)) && value != null) {
51
+ return value;
52
+ }
53
+
54
+ return timeStringArray[0];
55
+ }
56
+
57
+ private getOptionsForTimeUnit(timeUnit: ITimeUnit): Array<string> {
58
+ let format12HourArr = range(1, 13);
59
+ format12HourArr.unshift(format12HourArr.pop() as number);
60
+
61
+ const timeUnitArray = (() => {
62
+ if (timeUnit === 'hours') {
63
+ if (this.is12HourFormat) {
64
+ return format12HourArr;
65
+ } else {
66
+ return range(24);
67
+ }
68
+ } else {
69
+ return range(60);
70
+ }
71
+ })();
72
+
73
+ return timeUnitArray
74
+ .filter((item) => !(this.props.disabledOptions[timeUnit] ?? []).includes(item))
75
+ .map((value) => padStart(value.toString(), 2, '0'));
76
+ }
77
+
78
+ private handleTimeChange(index: number, newValue: string) {
79
+ let current = this.props.value.split(':');
80
+
81
+ const updated12HourValue = (() => {
82
+ if (parseInt(current[0], 10) >= 12) {
83
+ if (newValue === '12') {
84
+ return newValue;
85
+ } else {
86
+ return (parseInt(newValue, 10) + 12).toString();
87
+ }
88
+ } else {
89
+ if (newValue === '12') {
90
+ return '00';
91
+ } else {
92
+ return newValue;
93
+ }
94
+ }
95
+ })();
96
+
97
+ current[index] = this.is12HourFormat ? updated12HourValue : newValue;
98
+
99
+ this.props.onChange(current.join(':'));
100
+ }
101
+
102
+ componentDidMount(): void {
103
+ const correctedTime = [
104
+ this.getCorrectedTime('hours', this.getOptionsForTimeUnit('hours')),
105
+ ':',
106
+ this.getCorrectedTime('minutes', this.getOptionsForTimeUnit('minutes')),
107
+ this.props.allowSeconds
108
+ ? `:${this.getCorrectedTime('seconds', this.getOptionsForTimeUnit('seconds'))}`
109
+ : '',
110
+ ].join('');
111
+
112
+ if (this.props.value !== correctedTime) {
113
+ this.props.onChange(correctedTime);
114
+ }
115
+ }
116
+
117
+ padValue(value: number) {
118
+ return padStart((value).toString(), 2, '0');
119
+ }
120
+
121
+ updatedTimeUnit() {
122
+ const timeUnitValuesArray = this.props.value.split(':');
123
+
124
+ /**
125
+ * updating the initial value from props
126
+ */
127
+ if (this.is12HourFormat) {
128
+ if (parseInt(timeUnitValuesArray[0], 10) > 12) {
129
+ timeUnitValuesArray[0] = this.padValue(parseInt(timeUnitValuesArray[0], 10) - 12);
130
+ }
131
+ }
132
+
133
+ return timeUnitValuesArray;
134
+ }
135
+
136
+ render() {
137
+ const timeUnitValuesArray = this.updatedTimeUnit();
138
+
139
+ return (
140
+ <InputWrapper
141
+ label={this.props.label}
142
+ error={this.props.error}
143
+ invalid={this.props.error != null}
144
+ required={this.props.required}
145
+ disabled={this.props.disabled}
146
+ info={this.props.info}
147
+ inlineLabel={this.props.inlineLabel}
148
+ labelHidden={this.props.labelHidden}
149
+ tabindex={this.props.tabindex}
150
+ >
151
+ <div className='sd__input__time-picker-v2' data-test-id={this.props['data-test-id']}>
152
+ <div className='input-wrapper__time-picker-v2'>
153
+ <select
154
+ className='sd-input__select'
155
+ value={timeUnitValuesArray[0]}
156
+ onChange={({target}) => {
157
+ this.handleTimeChange(0, target.value);
158
+ }}
159
+ >
160
+ {this.getOptionsForTimeUnit('hours').map((hour) => (
161
+ <option value={hour} label={hour} key={hour} />
162
+ ))}
163
+ </select>
164
+ <span className='time-picker-v2-suffix'>:</span>
165
+ </div>
166
+ <div className='input-wrapper__time-picker-v2'>
167
+ <select
168
+ className='sd-input__select'
169
+ value={timeUnitValuesArray[1]}
170
+ onChange={({target}) => {
171
+ this.handleTimeChange(1, target.value);
172
+ }}
173
+ >
174
+ {this.getOptionsForTimeUnit('minutes').map((minute) => (
175
+ <option value={minute} label={minute} key={minute} />
176
+ ))}
177
+ </select>
178
+ {this.props.allowSeconds && (<span className='time-picker-v2-suffix'>:</span>)}
179
+ </div>
180
+ {this.props.allowSeconds && (
181
+ <div className='input-wrapper__time-picker-v2'>
182
+ <select
183
+ className='sd-input__select'
184
+ value={timeUnitValuesArray[2]}
185
+ onChange={({target}) => {
186
+ this.handleTimeChange(2, target.value);
187
+ }}
188
+ >
189
+ {this.getOptionsForTimeUnit('seconds').map((second) => (
190
+ <option value={second} label={second} key={second} />
191
+ ))}
192
+ </select>
193
+ </div>
194
+ )}
195
+ {this.is12HourFormat && (
196
+ <div className='input-wrapper__time-picker-v2'>
197
+ <span className='time-picker-v2-suffix' />
198
+ <select
199
+ className='sd-input__select'
200
+ value={(parseInt(this.props.value.split(':')[0], 10) >= 12) ? 'PM' : 'AM'}
201
+ onChange={({target}) => {
202
+ let splitValue = this.props.value.split(':');
203
+
204
+ if (target.value === 'PM') {
205
+ splitValue[0] = this.padValue(parseInt(splitValue[0], 10) + 12);
206
+ } else {
207
+ splitValue[0] = this.padValue(parseInt(splitValue[0], 10) - 12);
208
+ }
209
+
210
+ this.props.onChange(splitValue.join(':'));
211
+ }}
212
+ >
213
+ <option value='AM' label='AM' />
214
+ <option value='PM' label='PM' />
215
+ </select>
216
+ </div>
217
+ )}
218
+ </div>
219
+ </InputWrapper>
220
+ );
221
+ }
222
+ }
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import classNames from 'classnames';
3
+ import nextId from "react-id-generator";
3
4
  import {IPropsCustomHeader} from "../ToggleBox/index";
4
5
 
5
6
  interface IState {
@@ -7,6 +8,7 @@ interface IState {
7
8
  }
8
9
 
9
10
  export class CustomHeaderToggleBox extends React.PureComponent<IPropsCustomHeader, IState> {
11
+ htmlId = nextId('togglebox-');
10
12
  constructor(props: IPropsCustomHeader) {
11
13
  super(props);
12
14
  this.state = {
@@ -24,10 +26,12 @@ export class CustomHeaderToggleBox extends React.PureComponent<IPropsCustomHeade
24
26
  let classes = classNames('sd-shadow--z1 new-collapse-box', {
25
27
  'new-collapse-box--open': this.state.isOpen,
26
28
  });
29
+ const { isOpen } = this.state;
27
30
 
28
31
  return (
29
32
  <div
30
33
  className={classes}
34
+ aria-expanded={isOpen}
31
35
  data-test-id='toggle-box'
32
36
  >
33
37
  <div className='new-collapse-box__header'>
@@ -38,6 +42,7 @@ export class CustomHeaderToggleBox extends React.PureComponent<IPropsCustomHeade
38
42
  <button
39
43
  className='new-collapse-box__divider'
40
44
  onClick={this.toggle}
45
+ aria-controls={this.htmlId}
41
46
  >
42
47
  <span className='label label--translucent new-collapse-box__divider-label'>
43
48
  {this.props.toggleButtonLabel}
@@ -46,7 +51,7 @@ export class CustomHeaderToggleBox extends React.PureComponent<IPropsCustomHeade
46
51
  </div>
47
52
 
48
53
  <div className='new-collapse-box__content'>
49
- <div className='new-collapse-box__content-inner p-2 pt-0-5'>
54
+ <div id={this.htmlId} aria-hidden={!isOpen} className='new-collapse-box__content-inner p-2 pt-0-5'>
50
55
  {this.props.children}
51
56
  </div>
52
57
  </div>
@@ -14,7 +14,7 @@ interface IState {
14
14
  */
15
15
 
16
16
  export class SimpleToggleBox extends React.PureComponent<IPropsSimple, IState> {
17
- htmlId = nextId();
17
+ htmlId = nextId('togglebox-');
18
18
  constructor(props: IPropsSimple) {
19
19
  super(props);
20
20
  this.state = {
@@ -45,10 +45,13 @@ export class SimpleToggleBox extends React.PureComponent<IPropsSimple, IState> {
45
45
  render() {
46
46
  let classes = classNames('toggle-box', {
47
47
  'toggle-box--margin-normal': this.props.margin === undefined,
48
+ 'toggle-box--large-title': this.props.largeTitle,
49
+ 'toggle-box--circle': this.props.circledChevron,
48
50
  [`toggle-box--margin-${this.props.margin}`]: this.props.margin,
49
51
  'hidden': !this.state.isOpen,
52
+ 'open': this.state.isOpen,
50
53
  }, this.props.className);
51
- const { title, hideUsingCSS, children, badge } = this.props;
54
+ const { title, children, badge } = this.props;
52
55
  const { isOpen } = this.state;
53
56
 
54
57
  return (
@@ -61,7 +64,8 @@ export class SimpleToggleBox extends React.PureComponent<IPropsSimple, IState> {
61
64
  role="button"
62
65
  tabIndex={0}
63
66
  onKeyDown={this.handleKeyDown}
64
- id={`togglebox-${this.htmlId}`}
67
+ aria-expanded={isOpen}
68
+ aria-controls={this.htmlId}
65
69
  >
66
70
  <div className="toggle-box__chevron">
67
71
  <i className="icon-chevron-right-thin" />
@@ -75,23 +79,9 @@ export class SimpleToggleBox extends React.PureComponent<IPropsSimple, IState> {
75
79
  {badge ? badge : null}
76
80
  </a>
77
81
  <div className="toggle-box__content-wraper">
78
- {isOpen && !hideUsingCSS && (
79
- <div className="toggle-box__content" aria-describedby={`togglebox-${this.htmlId}`}>
80
- {children}
81
- </div>
82
- )}
83
-
84
- {hideUsingCSS && (
85
- <div
86
- className={classNames(
87
- 'toggle-box__content',
88
- { 'toggle-box__content--hidden': !isOpen },
89
- )}
90
- aria-describedby={`togglebox-${this.htmlId}`}
91
- >
92
- {children}
93
- </div>
94
- )}
82
+ <div id={this.htmlId} className="toggle-box__content" aria-hidden={!isOpen}>
83
+ {children}
84
+ </div>
95
85
  </div>
96
86
  </div>
97
87
  );
@@ -7,8 +7,10 @@ export interface IPropsSimple {
7
7
  title: string;
8
8
  badge?: JSX.Element;
9
9
  children: React.ReactNode;
10
- hideUsingCSS?: boolean;
10
+ circledChevron?: boolean;
11
11
  initiallyOpen?: boolean; // defaults to false
12
+ largeTitle?: boolean;
13
+
12
14
  className?: string;
13
15
  margin?: 'none' | 'small' | 'normal' | 'large';
14
16
  onOpen?(): void;
@@ -285,11 +285,9 @@ export class TreeMenu<T> extends React.Component<IProps<T>, IState<T>> {
285
285
 
286
286
  const item = this.state.buttonTree.pop();
287
287
 
288
- if (item != null) {
289
- this.setState({
290
- buttonValue: item,
291
- });
292
- }
288
+ this.setState({
289
+ buttonValue: item ?? null,
290
+ });
293
291
  }
294
292
 
295
293
  recursion(arr: Array<ITreeMenuNode<T>>) {
@@ -414,6 +412,7 @@ export class TreeMenu<T> extends React.Component<IProps<T>, IState<T>> {
414
412
  <ul
415
413
  ref={this.ref}
416
414
  className="suggestion-list suggestion-list--multi-select"
415
+ role='tree'
417
416
  >
418
417
  {this.state.options.map((option, i: React.Key | undefined) => (
419
418
  <TreeSelectItem
@@ -425,6 +424,10 @@ export class TreeMenu<T> extends React.Component<IProps<T>, IState<T>> {
425
424
  }}
426
425
  disabledItem={disabledItem(option)}
427
426
  getBorderColor={this.props.getBorderColor}
427
+ parentCategory={this.state.buttonValue == null
428
+ ? undefined
429
+ : this.props.getLabel(this.state.buttonValue.value)
430
+ }
428
431
  getBackgroundColor={this.props.getBackgroundColor}
429
432
  getId={this.props.getId}
430
433
  optionTemplate={this.props.optionTemplate}