@centreon/ui 23.10.117 → 23.10.119

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 (63) hide show
  1. package/package.json +12 -14
  2. package/src/Button/Save/StartIcon.tsx +3 -3
  3. package/src/Dashboard/Item.tsx +1 -1
  4. package/src/FileDropZone/index.tsx +1 -3
  5. package/src/Form/Inputs/CheckboxGroup.tsx +4 -1
  6. package/src/Form/Inputs/index.tsx +1 -1
  7. package/src/Graph/LineChart/BasicComponents/Lines/Threshold/index.tsx +4 -5
  8. package/src/Graph/LineChart/BasicComponents/useFilterLines.ts +1 -1
  9. package/src/Graph/LineChart/InteractiveComponents/Annotations/Annotation/index.tsx +3 -2
  10. package/src/Graph/LineChart/InteractiveComponents/Annotations/EventAnnotations.tsx +1 -1
  11. package/src/Graph/LineChart/Legend/useLegend.ts +5 -5
  12. package/src/Graph/LineChart/helpers/doc.ts +13 -16
  13. package/src/Graph/LineChart/helpers/index.ts +1 -1
  14. package/src/Graph/LineChart/index.stories.tsx +2 -1
  15. package/src/Graph/Text/Text.stories.tsx +4 -60
  16. package/src/Graph/common/timeSeries/index.ts +3 -3
  17. package/src/InputField/Select/Autocomplete/Connected/index.tsx +7 -10
  18. package/src/InputField/Select/Autocomplete/Draggable/SortableList.tsx +1 -1
  19. package/src/InputField/Select/Autocomplete/Draggable/SortableListContent.tsx +1 -1
  20. package/src/InputField/Select/Autocomplete/Draggable/index.tsx +1 -1
  21. package/src/InputField/Select/IconPopover/index.tsx +2 -2
  22. package/src/InputField/Select/index.tsx +1 -1
  23. package/src/Listing/Header/ListingHeader.tsx +1 -1
  24. package/src/Listing/index.stories.tsx +1 -12
  25. package/src/Listing/index.tsx +1 -1
  26. package/src/RichTextEditor/RichTextEditor.cypress.spec.tsx +52 -100
  27. package/src/RichTextEditor/RichTextEditor.tsx +4 -20
  28. package/src/RichTextEditor/plugins/ToolbarPlugin/AlignPicker.tsx +20 -21
  29. package/src/RichTextEditor/plugins/ToolbarPlugin/BlockButtons.tsx +77 -35
  30. package/src/RichTextEditor/plugins/ToolbarPlugin/FormatButtons.tsx +83 -29
  31. package/src/RichTextEditor/plugins/ToolbarPlugin/MacrosButton.tsx +39 -23
  32. package/src/RichTextEditor/plugins/ToolbarPlugin/ToolbarPlugin.styles.ts +14 -24
  33. package/src/RichTextEditor/plugins/ToolbarPlugin/UndoRedoButtons.tsx +0 -6
  34. package/src/RichTextEditor/plugins/ToolbarPlugin/index.tsx +10 -5
  35. package/src/RichTextEditor/translatedLabels.ts +0 -3
  36. package/src/SortableItems/index.tsx +7 -2
  37. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +3 -8
  38. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/models.ts +2 -0
  39. package/src/TimePeriods/DateTimePickerInput.tsx +17 -45
  40. package/src/TimePeriods/TimePeriods.cypress.spec.tsx +33 -9
  41. package/src/TimePeriods/helpers/index.ts +1 -1
  42. package/src/api/QueryProvider.tsx +1 -1
  43. package/src/api/TestQueryProvider.tsx +1 -1
  44. package/src/api/useFetchQuery/index.ts +24 -28
  45. package/src/api/useMutationQuery/index.ts +17 -39
  46. package/src/components/DataTable/Item/DataTableItem.tsx +2 -2
  47. package/src/components/Form/AccessRights/__fixtures__/contactAccessRight.mock.ts +0 -2
  48. package/src/components/Form/AccessRights/useAccessRightsForm.utils.ts +1 -1
  49. package/src/components/Form/Dashboard/DashboardForm.tsx +12 -15
  50. package/src/components/ItemComposition/ItemComposition.tsx +2 -4
  51. package/src/components/Menu/Button/MenuButton.tsx +2 -4
  52. package/src/components/Menu/MenuItem.tsx +3 -5
  53. package/src/components/Menu/MenuItems.tsx +3 -4
  54. package/src/index.ts +0 -1
  55. package/src/queryParameters/url/index.ts +1 -5
  56. package/src/screens/dashboard/DashboardsDetail.stories.tsx +108 -0
  57. package/src/screens/dashboard/DashboardsOverview.stories.tsx +281 -0
  58. package/src/utils/index.ts +1 -0
  59. package/src/utils/useDateTimePickerAdapter.ts +309 -0
  60. package/src/utils/{useLicenseExpirationWarning.test.tsx → useLicenseExpirationWarning.cypress.spec.tsx} +37 -48
  61. package/src/utils/useLicenseExpirationWarning.ts +18 -18
  62. package/src/RichTextEditor/plugins/ToolbarPlugin/LinkButton.tsx +0 -106
  63. package/src/RichTextEditor/plugins/ToolbarPlugin/ListButton.tsx +0 -155
@@ -1,6 +1,5 @@
1
1
  import RichTextEditor from './RichTextEditor';
2
2
  import { standardMacros } from './plugins/ToolbarPlugin/MacrosButton';
3
- import { labelAlignPicker } from './translatedLabels';
4
3
 
5
4
  interface CheckElementStyleOnRichTextEditorProps {
6
5
  check: boolean;
@@ -48,30 +47,16 @@ describe('Rich Text Editor', () => {
48
47
  it('displays all elements when RichTextEditor is called with required props', () => {
49
48
  cy.findByLabelText('Undo').should('be.visible');
50
49
  cy.findByLabelText('Redo').should('be.visible');
51
- cy.findByLabelText('align picker').should('be.visible');
52
- cy.findByLabelText('block').should('be.visible');
53
- cy.findByLabelText('format').should('be.visible');
50
+ cy.findByLabelText('bold').should('be.visible');
51
+ cy.findByLabelText('italic').should('be.visible');
52
+ cy.findByLabelText('underline').should('be.visible');
53
+ cy.findByLabelText('strikethrough').should('be.visible');
54
54
  cy.findByLabelText('link').should('be.visible');
55
- cy.findByLabelText('list').should('be.visible');
56
-
57
55
  cy.findByLabelText('RichTextEditor')
58
56
  .should('be.visible')
59
57
  .and('have.value', '');
60
58
 
61
59
  cy.findByLabelText('RichTextEditor').parent().contains('Type here...');
62
-
63
- cy.makeSnapshot();
64
- });
65
-
66
- it('displays all format actions when the format button is clicked', () => {
67
- cy.findByLabelText('format').click();
68
-
69
- cy.findByLabelText('bold').should('be.visible');
70
- cy.findByLabelText('italic').should('be.visible');
71
- cy.findByLabelText('underline').should('be.visible');
72
- cy.findByLabelText('strikethrough').should('be.visible');
73
-
74
- cy.makeSnapshot();
75
60
  });
76
61
 
77
62
  it('displays changes with undo redo buttons', () => {
@@ -95,36 +80,21 @@ describe('Rich Text Editor', () => {
95
80
 
96
81
  it('displays changes with bold, italic, underline and strikethrough buttons', () => {
97
82
  cy.get('[data-testid="RichTextEditor"]').type('cypress test');
98
-
99
83
  cy.get('[data-testid="RichTextEditor"]').focus().type('{selectAll}');
100
84
 
101
- cy.findByLabelText('format').click();
102
- cy.findByLabelText('bold').click();
103
-
104
- cy.findByLabelText('format').click();
105
- cy.findByLabelText('italic').click();
106
-
107
- cy.findByLabelText('format').click();
108
- cy.findByLabelText('underline').click();
109
-
110
- cy.findByLabelText('format').click();
111
- cy.findByLabelText('strikethrough').click();
85
+ cy.get('#bold').click();
86
+ cy.get('#italic').click();
87
+ cy.get('#underline').click();
88
+ cy.get('#strikethrough').click();
112
89
 
113
90
  cy.findByTestId('RichTextEditor').then((element: HTMLElement) => {
114
91
  checkElementStyleOnRichTextEditor({ check: true, element });
115
92
  });
116
93
 
117
- cy.findByLabelText('format').click();
118
- cy.findByLabelText('bold').click();
119
-
120
- cy.findByLabelText('format').click();
121
- cy.findByLabelText('italic').click();
122
-
123
- cy.findByLabelText('format').click();
124
- cy.findByLabelText('underline').click();
125
-
126
- cy.findByLabelText('format').click();
127
- cy.findByLabelText('strikethrough').click();
94
+ cy.get('#bold').click();
95
+ cy.get('#italic').click();
96
+ cy.get('#underline').click();
97
+ cy.get('#strikethrough').click();
128
98
 
129
99
  cy.findByTestId('RichTextEditor').then((element: HTMLElement) => {
130
100
  checkElementStyleOnRichTextEditor({ check: false, element });
@@ -135,7 +105,7 @@ describe('Rich Text Editor', () => {
135
105
  cy.get('[data-testid="RichTextEditor"]').type('cypress');
136
106
  cy.get('[data-testid="RichTextEditor"]').focus().type('{selectAll}');
137
107
 
138
- cy.findByLabelText('link').click();
108
+ cy.get('#link').click();
139
109
 
140
110
  cy.findByLabelText('Saved link').should('have.text', 'https://');
141
111
 
@@ -160,7 +130,7 @@ describe('Rich Text Editor', () => {
160
130
 
161
131
  cy.get('[data-testid="RichTextEditor"]').focus().type('{selectAll}');
162
132
 
163
- cy.findByLabelText('link').click();
133
+ cy.get('#link').click({ force: true });
164
134
 
165
135
  cy.findByText('cypress')
166
136
  .parent()
@@ -183,7 +153,7 @@ describe('Rich Text Editor', () => {
183
153
  cy.get('[data-testid="RichTextEditor"]').type('cypress');
184
154
  cy.get('[data-testid="RichTextEditor"]').focus().type('{selectAll}');
185
155
 
186
- cy.findByLabelText('link').click();
156
+ cy.get('#link').click();
187
157
 
188
158
  cy.findByLabelText('Saved link').should('have.text', 'https://');
189
159
 
@@ -219,11 +189,11 @@ describe('Rich Text Editor', () => {
219
189
  it('displays editor when editable props is false and an initialState exist', () => {
220
190
  cy.findByLabelText('Undo').should('not.exist');
221
191
  cy.findByLabelText('Redo').should('not.exist');
222
- cy.findByLabelText(labelAlignPicker).should('not.exist');
223
- cy.findByLabelText('block').should('not.exist');
224
- cy.findByLabelText('format').should('not.exist');
192
+ cy.findByLabelText('bold').should('not.exist');
193
+ cy.findByLabelText('italic').should('not.exist');
194
+ cy.findByLabelText('underline').should('not.exist');
195
+ cy.findByLabelText('strikethrough').should('not.exist');
225
196
  cy.findByLabelText('link').should('not.exist');
226
- cy.findByLabelText('list').should('not.exist');
227
197
  });
228
198
  });
229
199
 
@@ -243,11 +213,11 @@ describe('Rich Text Editor', () => {
243
213
  it('displays editor when editable props is false and an initialState exist', () => {
244
214
  cy.findByLabelText('Undo').should('be.disabled');
245
215
  cy.findByLabelText('Redo').should('be.disabled');
246
- cy.findByLabelText(labelAlignPicker).should('be.disabled');
247
- cy.findByLabelText('block').should('be.disabled');
248
- cy.findByLabelText('format').should('be.disabled');
216
+ cy.findByLabelText('bold').should('be.disabled');
217
+ cy.findByLabelText('italic').should('be.disabled');
218
+ cy.findByLabelText('underline').should('be.disabled');
219
+ cy.findByLabelText('strikethrough').should('be.disabled');
249
220
  cy.findByLabelText('link').should('be.disabled');
250
- cy.findByLabelText('list').should('be.disabled');
251
221
  });
252
222
  });
253
223
 
@@ -292,6 +262,7 @@ describe('Rich Text Editor', () => {
292
262
  });
293
263
 
294
264
  it('displays the Macros button when the "displayMacrosButton" prop is set to true', () => {
265
+ cy.findByLabelText('Macros').should('be.visible');
295
266
  cy.findByLabelText('Macros').click();
296
267
 
297
268
  standardMacros.forEach((macro) => {
@@ -302,10 +273,10 @@ describe('Rich Text Editor', () => {
302
273
  it('ensures that the selected macro is inserted into the RichTextEditor', () => {
303
274
  cy.get('[data-testid="RichTextEditor"]').type('macro : ');
304
275
 
305
- standardMacros.forEach((macro) => {
306
- cy.findByLabelText('Macros').click();
307
- cy.findByText(macro).click();
276
+ cy.findByLabelText('Macros').click({});
308
277
 
278
+ standardMacros.forEach((macro) => {
279
+ cy.findByText(macro).should('be.visible').click({ force: true });
309
280
  cy.get('[data-testid="RichTextEditor"]').should('contain', macro);
310
281
  });
311
282
  });
@@ -320,59 +291,44 @@ describe('Rich Text Editor', () => {
320
291
 
321
292
  const testCases = [
322
293
  {
323
- blockType: 'Huge',
294
+ blockType: 'Heading 1',
295
+ tag: 'h1'
296
+ },
297
+ {
298
+ blockType: 'Heading 2',
299
+ tag: 'h2'
300
+ },
301
+ {
302
+ blockType: 'Heading 3',
324
303
  tag: 'h3'
325
304
  },
326
305
  {
327
- blockType: 'Large',
328
- tag: 'h5'
306
+ blockType: 'Heading 4',
307
+ tag: 'h4'
329
308
  },
330
309
  {
331
- blockType: 'Normal',
332
- tag: 'p'
310
+ blockType: 'Heading 5',
311
+ tag: 'h5'
333
312
  },
334
313
  {
335
- blockType: 'Small',
314
+ blockType: 'Heading 6',
336
315
  tag: 'h6'
337
- }
338
- ];
339
-
340
- testCases.forEach(({ blockType, tag }) => {
341
- it(`displays ${blockType} when the corresponding block type button is selected`, () => {
342
- cy.get('[data-testid="RichTextEditor"]').type('Example');
343
- cy.findByLabelText('block').click();
344
- cy.findByText(blockType).click();
345
-
346
- cy.get('[data-testid="RichTextEditor"]').find(tag).should('be.visible');
347
-
348
- cy.makeSnapshot();
349
- });
350
- });
351
- });
352
-
353
- describe('List type', () => {
354
- beforeEach(() => {
355
- cy.mount({
356
- Component: <RichTextEditor editable />
357
- });
358
- });
359
-
360
- const testCases = [
316
+ },
361
317
  {
362
- listType: 'Unordered List',
318
+ blockType: 'Bullet List',
363
319
  tag: 'ul'
364
320
  },
365
321
  {
366
- listType: 'Ordered List',
322
+ blockType: 'Number List',
367
323
  tag: 'ol'
368
324
  }
369
325
  ];
370
326
 
371
- testCases.forEach(({ listType, tag }) => {
372
- it(`displays ${listType} when the corresponding list type button is selected`, () => {
327
+ testCases.forEach(({ blockType, tag }) => {
328
+ it(`displays ${blockType} when the corresponding block type button is selected`, () => {
373
329
  cy.get('[data-testid="RichTextEditor"]').type('Example');
374
- cy.findByLabelText('list').click();
375
- cy.findByLabelText(listType).click();
330
+ cy.findByTestId('Block type').click();
331
+ cy.findByText(blockType).click();
376
332
 
377
333
  cy.get('[data-testid="RichTextEditor"]').find(tag).should('be.visible');
378
334
 
@@ -388,18 +344,14 @@ describe('Rich Text Editor', () => {
388
344
  });
389
345
  });
390
346
 
391
- const testCases = ['Center', 'Right', 'Left'];
347
+ const testCases = ['Center', 'Right'];
392
348
 
393
349
  testCases.forEach((label) => {
394
- it(`aligns the text to the ${label} when the corresponding button was clicked`, () => {
350
+ it(`aligns the text to the ${label} when the corresponding button is clicked`, () => {
395
351
  cy.get('[data-testid="RichTextEditor"]').type('Example');
396
352
 
397
- cy.findByLabelText(labelAlignPicker).click();
398
- cy.findByLabelText(label).click();
399
-
400
- cy.findByText('Example')
401
- .parent()
402
- .should('have.css', 'text-align', label.toLowerCase());
353
+ cy.findByLabelText('left').click();
354
+ cy.contains(label).click();
403
355
 
404
356
  cy.makeSnapshot();
405
357
  });
@@ -12,7 +12,6 @@ import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
12
12
  import { equals } from 'ramda';
13
13
  import { ListPlugin } from '@lexical/react/LexicalListPlugin';
14
14
  import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
15
- import { $generateHtmlFromNodes } from '@lexical/html';
16
15
 
17
16
  import { Typography } from '@mui/material';
18
17
 
@@ -39,7 +38,6 @@ export interface RichTextEditorProps {
39
38
  openLinkInNewTab?: boolean;
40
39
  placeholder?: string;
41
40
  resetEditorToInitialStateCondition?: () => boolean;
42
- setHtmlString?: (htmlString: string) => void;
43
41
  toolbarClassName?: string;
44
42
  toolbarPositions?: 'start' | 'end';
45
43
  }
@@ -94,9 +92,9 @@ const useStyles = makeStyles<{ toolbarPositions: 'start' | 'end' }>()(
94
92
  lineHeight: theme.typography.h5.lineHeight
95
93
  },
96
94
  h6: {
97
- fontSize: theme.typography.body2.fontSize,
98
- fontWeight: theme.typography.body2.fontWeight,
99
- lineHeight: theme.typography.body2.lineHeight
95
+ fontSize: theme.typography.h6.fontSize,
96
+ fontWeight: theme.typography.h6.fontWeight,
97
+ lineHeight: theme.typography.h6.lineHeight
100
98
  },
101
99
  italic: {
102
100
  fontStyle: 'italic'
@@ -104,11 +102,6 @@ const useStyles = makeStyles<{ toolbarPositions: 'start' | 'end' }>()(
104
102
  link: {
105
103
  color: theme.palette.primary.main
106
104
  },
107
- paragraph: {
108
- fontSize: theme.typography.body1.fontSize,
109
- fontWeight: theme.typography.body1.fontWeight,
110
- lineHeight: theme.typography.body1.lineHeight
111
- },
112
105
  strikethrough: {
113
106
  textDecoration: 'line-through'
114
107
  },
@@ -145,7 +138,6 @@ const RichTextEditor = ({
145
138
  openLinkInNewTab = true,
146
139
  initialize,
147
140
  displayBlockButtons = true,
148
- setHtmlString,
149
141
  toolbarClassName
150
142
  }: RichTextEditorProps): JSX.Element => {
151
143
  const { classes } = useStyles({ toolbarPositions });
@@ -170,7 +162,6 @@ const RichTextEditor = ({
170
162
  h6: classes.h6
171
163
  },
172
164
  link: classes.link,
173
- paragraph: classes.paragraph,
174
165
  text: {
175
166
  bold: classes.bold,
176
167
  italic: classes.italic,
@@ -181,13 +172,6 @@ const RichTextEditor = ({
181
172
  }
182
173
  };
183
174
 
184
- const change = (state: EditorState, editor: LexicalEditor): void => {
185
- editor.update(() => {
186
- setHtmlString?.($generateHtmlFromNodes(editor, null));
187
- });
188
- getEditorState?.(state, editor);
189
- };
190
-
191
175
  return (
192
176
  <LexicalComposer initialConfig={initialConfig}>
193
177
  <div className={classes.container}>
@@ -228,7 +212,7 @@ const RichTextEditor = ({
228
212
  <HistoryPlugin />
229
213
  <LinkPlugin />
230
214
  <ListPlugin />
231
- <OnChangePlugin onChange={change} />
215
+ <OnChangePlugin onChange={getEditorState} />
232
216
  <AutoCompleteLinkPlugin openLinkInNewTab={openLinkInNewTab} />
233
217
  <FloatingLinkEditorPlugin
234
218
  editable={editable}
@@ -18,9 +18,8 @@ import { SvgIconTypeMap } from '@mui/material';
18
18
 
19
19
  import { Menu } from '../../../components';
20
20
  import { getSelectedNode } from '../../utils/getSelectedNode';
21
- import { labelAlignPicker } from '../../translatedLabels';
22
21
 
23
- import { useStyles } from './ToolbarPlugin.styles';
22
+ import { useAlignPickerStyles } from './ToolbarPlugin.styles';
24
23
 
25
24
  const formatOptions: Array<{
26
25
  Icon: OverridableComponent<SvgIconTypeMap<object, 'svg'>>;
@@ -49,7 +48,7 @@ interface Props {
49
48
  }
50
49
 
51
50
  const AlignPicker = ({ disabled }: Props): JSX.Element => {
52
- const { classes } = useStyles();
51
+ const { classes } = useAlignPickerStyles();
53
52
 
54
53
  const [elementFormat, setElementFormat] = useState<ElementFormatType>('left');
55
54
 
@@ -89,25 +88,25 @@ const AlignPicker = ({ disabled }: Props): JSX.Element => {
89
88
 
90
89
  return (
91
90
  <Menu>
92
- <Menu.Button
93
- ariaLabel={labelAlignPicker}
94
- className={classes.button}
95
- disabled={disabled}
96
- >
97
- {selectedFormat && <selectedFormat.Icon />}
91
+ <Menu.Button ariaLabel={elementFormat} disabled={disabled}>
92
+ {selectedFormat && (
93
+ <div className={classes.button}>
94
+ <selectedFormat.Icon /> {selectedFormat.label}
95
+ </div>
96
+ )}
98
97
  </Menu.Button>
99
- <Menu.Items className={classes.menuItems}>
100
- <div className={classes.menu}>
101
- {formatOptions.map(({ Icon, value, label }) => (
102
- <Menu.Item
103
- isActive={equals(value, elementFormat)}
104
- key={value}
105
- onClick={dispatchAlignment(value)}
106
- >
107
- <Icon aria-label={label} />
108
- </Menu.Item>
109
- ))}
110
- </div>
98
+ <Menu.Items>
99
+ {formatOptions.map(({ Icon, label, value }) => (
100
+ <Menu.Item
101
+ isActive={equals(value, elementFormat)}
102
+ key={value}
103
+ onClick={dispatchAlignment(value)}
104
+ >
105
+ <div className={classes.option}>
106
+ <Icon /> {label}
107
+ </div>
108
+ </Menu.Item>
109
+ ))}
111
110
  </Menu.Items>
112
111
  </Menu>
113
112
  );
@@ -1,6 +1,13 @@
1
1
  import { useCallback, useEffect, useState } from 'react';
2
2
 
3
3
  import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
4
+ import {
5
+ $isListNode,
6
+ INSERT_ORDERED_LIST_COMMAND,
7
+ INSERT_UNORDERED_LIST_COMMAND,
8
+ ListNode,
9
+ REMOVE_LIST_COMMAND
10
+ } from '@lexical/list';
4
11
  import {
5
12
  $createParagraphNode,
6
13
  $getSelection,
@@ -16,27 +23,43 @@ import {
16
23
  } from '@lexical/rich-text';
17
24
  import { $setBlocksType } from '@lexical/selection';
18
25
  import { T, always, cond, equals, isNil } from 'ramda';
19
- import { $findMatchingParent, mergeRegister } from '@lexical/utils';
20
- import { useTranslation } from 'react-i18next';
21
-
22
- import TextSizeIcon from '@mui/icons-material/TextFields';
26
+ import {
27
+ $findMatchingParent,
28
+ $getNearestNodeOfType,
29
+ mergeRegister
30
+ } from '@lexical/utils';
23
31
 
24
- import { Menu } from '../../../components';
32
+ import { SingleAutocompleteField } from '../../..';
25
33
 
26
- import { useStyles } from './ToolbarPlugin.styles';
34
+ import { useBlockButtonsStyles } from './ToolbarPlugin.styles';
27
35
 
28
36
  interface Props {
29
37
  disabled: boolean;
30
38
  }
31
39
 
32
40
  const blockTypeToBlockName = {
33
- h3: 'Huge',
34
- h5: 'Large',
35
- h6: 'Small',
41
+ bullet: 'Bullet List',
42
+ h1: 'Heading 1',
43
+ h2: 'Heading 2',
44
+ h3: 'Heading 3',
45
+ h4: 'Heading 4',
46
+ h5: 'Heading 5',
47
+ h6: 'Heading 6',
48
+ number: 'Number List',
36
49
  paragraph: 'Normal'
37
50
  };
38
51
 
39
- const blockTypes = ['h3', 'h5', 'paragraph', 'h6'];
52
+ const blockTypes = [
53
+ 'h1',
54
+ 'h2',
55
+ 'h3',
56
+ 'h4',
57
+ 'h5',
58
+ 'h6',
59
+ 'bullet',
60
+ 'number',
61
+ 'paragraph'
62
+ ];
40
63
 
41
64
  const blockTypeOptions = blockTypes.map((blockType) => ({
42
65
  id: blockType,
@@ -44,8 +67,7 @@ const blockTypeOptions = blockTypes.map((blockType) => ({
44
67
  }));
45
68
 
46
69
  const BlockButtons = ({ disabled }: Props): JSX.Element => {
47
- const { classes } = useStyles();
48
- const { t } = useTranslation();
70
+ const { classes } = useBlockButtonsStyles();
49
71
 
50
72
  const [blockType, setBlockType] =
51
73
  useState<keyof typeof blockTypeToBlockName>('paragraph');
@@ -59,6 +81,22 @@ const BlockButtons = ({ disabled }: Props): JSX.Element => {
59
81
  });
60
82
  };
61
83
 
84
+ const formatBulletList = (): void => {
85
+ if (blockType !== 'bullet') {
86
+ editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);
87
+ } else {
88
+ editor.dispatchCommand(REMOVE_LIST_COMMAND, undefined);
89
+ }
90
+ };
91
+
92
+ const formatNumberedList = (): void => {
93
+ if (blockType !== 'number') {
94
+ editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);
95
+ } else {
96
+ editor.dispatchCommand(REMOVE_LIST_COMMAND, undefined);
97
+ }
98
+ };
99
+
62
100
  const formatHeading = (headingSize: HeadingTagType): void => {
63
101
  if (blockType !== headingSize) {
64
102
  editor.update(() => {
@@ -70,13 +108,18 @@ const BlockButtons = ({ disabled }: Props): JSX.Element => {
70
108
  }
71
109
  };
72
110
 
73
- const changeBlockType = (newBlockType): void => {
111
+ const changeBlockType = (_, newBlockType): void => {
74
112
  const formatFunction = cond<Array<string>, (value?) => void>([
113
+ [equals('bullet'), always(formatBulletList)],
114
+ [equals('number'), always(formatNumberedList)],
115
+ [equals('h1'), always(() => formatHeading('h1'))],
116
+ [equals('h2'), always(() => formatHeading('h2'))],
75
117
  [equals('h3'), always(() => formatHeading('h3'))],
118
+ [equals('h4'), always(() => formatHeading('h4'))],
76
119
  [equals('h5'), always(() => formatHeading('h5'))],
77
120
  [equals('h6'), always(() => formatHeading('h6'))],
78
121
  [T, always(formatParagraph)]
79
- ])(newBlockType || '');
122
+ ])(newBlockType?.id || '');
80
123
 
81
124
  formatFunction();
82
125
  };
@@ -99,13 +142,23 @@ const BlockButtons = ({ disabled }: Props): JSX.Element => {
99
142
  return;
100
143
  }
101
144
 
102
- const type = $isHeadingNode(element) ? element.getTag() : element.getType();
145
+ if ($isListNode(element)) {
146
+ const parentList = $getNearestNodeOfType(anchorNode, ListNode);
147
+ const type = parentList
148
+ ? parentList.getListType()
149
+ : element.getListType();
150
+ setBlockType(type);
103
151
 
152
+ return;
153
+ }
154
+ const type = $isHeadingNode(element) ? element.getTag() : element.getType();
104
155
  if (type in blockTypeToBlockName) {
105
156
  setBlockType(type as keyof typeof blockTypeToBlockName);
106
157
  }
107
158
  }, [editor]);
108
159
 
160
+ const value = blockTypeOptions.find((option) => option.id === blockType);
161
+
109
162
  useEffect(() => {
110
163
  return editor.registerCommand(
111
164
  SELECTION_CHANGE_COMMAND,
@@ -129,26 +182,15 @@ const BlockButtons = ({ disabled }: Props): JSX.Element => {
129
182
  }, [editor, updateToolbar]);
130
183
 
131
184
  return (
132
- <Menu>
133
- <Menu.Button
134
- ariaLabel="block"
135
- className={classes.button}
136
- disabled={disabled}
137
- >
138
- <TextSizeIcon />
139
- </Menu.Button>
140
- <Menu.Items className={classes.menuItems}>
141
- {blockTypeOptions.map(({ id, name }) => (
142
- <Menu.Item
143
- isActive={equals(id, blockType)}
144
- key={name}
145
- onClick={() => changeBlockType(id)}
146
- >
147
- {t(name)}
148
- </Menu.Item>
149
- ))}
150
- </Menu.Items>
151
- </Menu>
185
+ <SingleAutocompleteField
186
+ className={classes.autocomplete}
187
+ dataTestId="Block type"
188
+ disabled={disabled}
189
+ label=""
190
+ options={blockTypeOptions}
191
+ value={value}
192
+ onChange={changeBlockType}
193
+ />
152
194
  );
153
195
  };
154
196