@squiz/formatted-text-editor 1.12.0-alpha.38 → 1.12.0-alpha.40

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/README.md CHANGED
@@ -51,6 +51,8 @@ Or if you'd like to "watch" for changes:
51
51
  npm run test:watch
52
52
  ```
53
53
 
54
+ When testing text input in the text editor, it is recommended to use the `<MockEditor />` component, which is a simple wrapper around the Remirror component that provides additional functionality for testing (supplying text into the editor).
55
+
54
56
  ### End to end testing
55
57
 
56
58
  This package uses [Cypress](https://docs.cypress.io/) for end to end testing.
@@ -1,21 +1,10 @@
1
1
  import React from 'react';
2
- import { BoldExtension, HeadingExtension, ItalicExtension, NodeFormattingExtension, ParagraphExtension, UnderlineExtension, HistoryExtension, wysiwygPreset, } from 'remirror/extensions';
3
2
  import { EditorComponent, Remirror, useRemirror } from '@remirror/react';
4
3
  import { EditorToolbar } from '../EditorToolbar/EditorToolbar';
5
- import { PreformattedExtension } from '../extensions/PreformattedExtension/PreformattedExtension';
4
+ import { Extensions } from '../Extensions/Extensions';
6
5
  const Editor = ({ content }) => {
7
6
  const { manager, state, setState } = useRemirror({
8
- extensions: () => [
9
- ...wysiwygPreset(),
10
- new BoldExtension(),
11
- new HeadingExtension(),
12
- new ItalicExtension(),
13
- new NodeFormattingExtension(),
14
- new ParagraphExtension(),
15
- new PreformattedExtension(),
16
- new UnderlineExtension(),
17
- new HistoryExtension(),
18
- ],
7
+ extensions: Extensions,
19
8
  content,
20
9
  selection: 'start',
21
10
  stringHandler: 'html',
@@ -0,0 +1,3 @@
1
+ import { BoldExtension, HeadingExtension, NodeFormattingExtension, HistoryExtension } from 'remirror/extensions';
2
+ import { Extension } from '@remirror/core';
3
+ export declare const Extensions: () => (BoldExtension | HistoryExtension | HeadingExtension | NodeFormattingExtension | Extension<import("@remirror/core").EmptyShape>)[];
@@ -0,0 +1,13 @@
1
+ import { BoldExtension, HeadingExtension, ItalicExtension, NodeFormattingExtension, ParagraphExtension, UnderlineExtension, HistoryExtension, wysiwygPreset, } from 'remirror/extensions';
2
+ import { PreformattedExtension } from './PreformattedExtension/PreformattedExtension';
3
+ export const Extensions = () => [
4
+ ...wysiwygPreset(),
5
+ new BoldExtension(),
6
+ new HeadingExtension(),
7
+ new ItalicExtension(),
8
+ new NodeFormattingExtension(),
9
+ new ParagraphExtension(),
10
+ new PreformattedExtension(),
11
+ new UnderlineExtension(),
12
+ new HistoryExtension(),
13
+ ];
package/lib/index.css CHANGED
@@ -3882,7 +3882,7 @@ textarea {
3882
3882
  input::placeholder,
3883
3883
  textarea::placeholder {
3884
3884
  opacity: 1;
3885
- color: #9ca3af;
3885
+ color: #9CA3AF;
3886
3886
  }
3887
3887
  button,
3888
3888
  [role=button] {
@@ -3910,6 +3910,309 @@ video {
3910
3910
  [hidden] {
3911
3911
  display: none;
3912
3912
  }
3913
+ [type=text],
3914
+ [type=email],
3915
+ [type=url],
3916
+ [type=password],
3917
+ [type=number],
3918
+ [type=date],
3919
+ [type=datetime-local],
3920
+ [type=month],
3921
+ [type=search],
3922
+ [type=tel],
3923
+ [type=time],
3924
+ [type=week],
3925
+ [multiple],
3926
+ textarea,
3927
+ select {
3928
+ appearance: none;
3929
+ background-color: #fff;
3930
+ border-color: #6B7280;
3931
+ border-width: 1px;
3932
+ border-radius: 0px;
3933
+ padding-top: 0.5rem;
3934
+ padding-right: 0.75rem;
3935
+ padding-bottom: 0.5rem;
3936
+ padding-left: 0.75rem;
3937
+ font-size: 1rem;
3938
+ line-height: 1.5rem;
3939
+ --tw-shadow: 0 0 #0000;
3940
+ }
3941
+ [type=text]:focus,
3942
+ [type=email]:focus,
3943
+ [type=url]:focus,
3944
+ [type=password]:focus,
3945
+ [type=number]:focus,
3946
+ [type=date]:focus,
3947
+ [type=datetime-local]:focus,
3948
+ [type=month]:focus,
3949
+ [type=search]:focus,
3950
+ [type=tel]:focus,
3951
+ [type=time]:focus,
3952
+ [type=week]:focus,
3953
+ [multiple]:focus,
3954
+ textarea:focus,
3955
+ select:focus {
3956
+ outline: 2px solid transparent;
3957
+ outline-offset: 2px;
3958
+ --tw-ring-inset: var(--tw-empty, );
3959
+ --tw-ring-offset-width: 0px;
3960
+ --tw-ring-offset-color: #fff;
3961
+ --tw-ring-color: #1C64F2;
3962
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
3963
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
3964
+ box-shadow:
3965
+ var(--tw-ring-offset-shadow),
3966
+ var(--tw-ring-shadow),
3967
+ var(--tw-shadow);
3968
+ border-color: #1C64F2;
3969
+ }
3970
+ input::placeholder,
3971
+ textarea::placeholder {
3972
+ color: #6B7280;
3973
+ opacity: 1;
3974
+ }
3975
+ ::-webkit-datetime-edit-fields-wrapper {
3976
+ padding: 0;
3977
+ }
3978
+ ::-webkit-date-and-time-value {
3979
+ min-height: 1.5em;
3980
+ }
3981
+ select:not([size]) {
3982
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
3983
+ background-position: right 0.5rem center;
3984
+ background-repeat: no-repeat;
3985
+ background-size: 1.5em 1.5em;
3986
+ padding-right: 2.5rem;
3987
+ print-color-adjust: exact;
3988
+ }
3989
+ [multiple] {
3990
+ background-image: initial;
3991
+ background-position: initial;
3992
+ background-repeat: unset;
3993
+ background-size: initial;
3994
+ padding-right: 0.75rem;
3995
+ print-color-adjust: unset;
3996
+ }
3997
+ [type=checkbox],
3998
+ [type=radio] {
3999
+ appearance: none;
4000
+ padding: 0;
4001
+ print-color-adjust: exact;
4002
+ display: inline-block;
4003
+ vertical-align: middle;
4004
+ background-origin: border-box;
4005
+ user-select: none;
4006
+ flex-shrink: 0;
4007
+ height: 1rem;
4008
+ width: 1rem;
4009
+ color: #1C64F2;
4010
+ background-color: #fff;
4011
+ border-color: #6B7280;
4012
+ border-width: 1px;
4013
+ --tw-shadow: 0 0 #0000;
4014
+ }
4015
+ [type=checkbox] {
4016
+ border-radius: 0px;
4017
+ }
4018
+ [type=radio] {
4019
+ border-radius: 100%;
4020
+ }
4021
+ [type=checkbox]:focus,
4022
+ [type=radio]:focus {
4023
+ outline: 2px solid transparent;
4024
+ outline-offset: 2px;
4025
+ --tw-ring-inset: var(--tw-empty, );
4026
+ --tw-ring-offset-width: 2px;
4027
+ --tw-ring-offset-color: #fff;
4028
+ --tw-ring-color: #1C64F2;
4029
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
4030
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
4031
+ box-shadow:
4032
+ var(--tw-ring-offset-shadow),
4033
+ var(--tw-ring-shadow),
4034
+ var(--tw-shadow);
4035
+ }
4036
+ [type=checkbox]:checked,
4037
+ [type=radio]:checked,
4038
+ .dark [type=checkbox]:checked,
4039
+ .dark [type=radio]:checked {
4040
+ border-color: transparent;
4041
+ background-color: currentColor;
4042
+ background-size: 100% 100%;
4043
+ background-position: center;
4044
+ background-repeat: no-repeat;
4045
+ }
4046
+ [type=checkbox]:checked {
4047
+ background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
4048
+ }
4049
+ [type=radio]:checked {
4050
+ background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
4051
+ }
4052
+ [type=checkbox]:indeterminate {
4053
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
4054
+ border-color: transparent;
4055
+ background-color: currentColor;
4056
+ background-size: 100% 100%;
4057
+ background-position: center;
4058
+ background-repeat: no-repeat;
4059
+ }
4060
+ [type=checkbox]:indeterminate:hover,
4061
+ [type=checkbox]:indeterminate:focus {
4062
+ border-color: transparent;
4063
+ background-color: currentColor;
4064
+ }
4065
+ [type=file] {
4066
+ background: unset;
4067
+ border-color: inherit;
4068
+ border-width: 0;
4069
+ border-radius: 0;
4070
+ padding: 0;
4071
+ font-size: unset;
4072
+ line-height: inherit;
4073
+ }
4074
+ [type=file]:focus {
4075
+ outline: 1px auto inherit;
4076
+ }
4077
+ input[type=file]::file-selector-button {
4078
+ color: white;
4079
+ background: #1F2937;
4080
+ border: 0;
4081
+ font-weight: 500;
4082
+ font-size: 0.8125rem;
4083
+ cursor: pointer;
4084
+ padding-top: 0.625rem;
4085
+ padding-bottom: 0.625rem;
4086
+ padding-left: 2rem;
4087
+ padding-right: 1rem;
4088
+ margin-inline-start: -1rem;
4089
+ margin-inline-end: 1rem;
4090
+ }
4091
+ input[type=file]::file-selector-button:hover {
4092
+ background: #374151;
4093
+ }
4094
+ input[type=range]::-webkit-slider-thumb {
4095
+ height: 1.25rem;
4096
+ width: 1.25rem;
4097
+ background: #1C64F2;
4098
+ border-radius: 9999px;
4099
+ border: 0;
4100
+ appearance: none;
4101
+ -moz-appearance: none;
4102
+ -webkit-appearance: none;
4103
+ cursor: pointer;
4104
+ }
4105
+ input[type=range]:disabled::-webkit-slider-thumb {
4106
+ background: #9CA3AF;
4107
+ }
4108
+ input[type=range]:focus::-webkit-slider-thumb {
4109
+ outline: 2px solid transparent;
4110
+ outline-offset: 2px;
4111
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
4112
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);
4113
+ box-shadow:
4114
+ var(--tw-ring-offset-shadow),
4115
+ var(--tw-ring-shadow),
4116
+ var(--tw-shadow, 0 0 #0000);
4117
+ --tw-ring-opacity: 1px;
4118
+ --tw-ring-color: rgb(164 202 254 / var(--tw-ring-opacity));
4119
+ }
4120
+ input[type=range]::-moz-range-thumb {
4121
+ height: 1.25rem;
4122
+ width: 1.25rem;
4123
+ background: #1C64F2;
4124
+ border-radius: 9999px;
4125
+ border: 0;
4126
+ appearance: none;
4127
+ -moz-appearance: none;
4128
+ -webkit-appearance: none;
4129
+ cursor: pointer;
4130
+ }
4131
+ input[type=range]:disabled::-moz-range-thumb {
4132
+ background: #9CA3AF;
4133
+ }
4134
+ input[type=range]::-moz-range-progress {
4135
+ background: #3F83F8;
4136
+ }
4137
+ input[type=range]::-ms-fill-lower {
4138
+ background: #3F83F8;
4139
+ }
4140
+ [data-popper-arrow],
4141
+ [data-popper-arrow]:before {
4142
+ position: absolute;
4143
+ width: 8px;
4144
+ height: 8px;
4145
+ background: inherit;
4146
+ }
4147
+ [data-popper-arrow] {
4148
+ visibility: hidden;
4149
+ }
4150
+ [data-popper-arrow]:before {
4151
+ content: "";
4152
+ visibility: visible;
4153
+ transform: rotate(45deg);
4154
+ }
4155
+ [data-popper-arrow]:after {
4156
+ content: "";
4157
+ visibility: visible;
4158
+ transform: rotate(45deg);
4159
+ position: absolute;
4160
+ width: 9px;
4161
+ height: 9px;
4162
+ background: inherit;
4163
+ }
4164
+ [role=tooltip] > [data-popper-arrow]:before {
4165
+ border-style: solid;
4166
+ border-color: #e5e7eb;
4167
+ }
4168
+ [role=tooltip] > [data-popper-arrow]:after {
4169
+ border-style: solid;
4170
+ border-color: #e5e7eb;
4171
+ }
4172
+ [data-popover][role=tooltip][data-popper-placement^=top] > [data-popper-arrow]:before {
4173
+ border-bottom-width: 1px;
4174
+ border-right-width: 1px;
4175
+ }
4176
+ [data-popover][role=tooltip][data-popper-placement^=top] > [data-popper-arrow]:after {
4177
+ border-bottom-width: 1px;
4178
+ border-right-width: 1px;
4179
+ }
4180
+ [data-popover][role=tooltip][data-popper-placement^=right] > [data-popper-arrow]:before {
4181
+ border-bottom-width: 1px;
4182
+ border-left-width: 1px;
4183
+ }
4184
+ [data-popover][role=tooltip][data-popper-placement^=right] > [data-popper-arrow]:after {
4185
+ border-bottom-width: 1px;
4186
+ border-left-width: 1px;
4187
+ }
4188
+ [data-popover][role=tooltip][data-popper-placement^=bottom] > [data-popper-arrow]:before {
4189
+ border-top-width: 1px;
4190
+ border-left-width: 1px;
4191
+ }
4192
+ [data-popover][role=tooltip][data-popper-placement^=bottom] > [data-popper-arrow]:after {
4193
+ border-top-width: 1px;
4194
+ border-left-width: 1px;
4195
+ }
4196
+ [data-popover][role=tooltip][data-popper-placement^=left] > [data-popper-arrow]:before {
4197
+ border-top-width: 1px;
4198
+ border-right-width: 1px;
4199
+ }
4200
+ [data-popover][role=tooltip][data-popper-placement^=left] > [data-popper-arrow]:after {
4201
+ border-top-width: 1px;
4202
+ border-right-width: 1px;
4203
+ }
4204
+ [data-popover][role=tooltip][data-popper-placement^=top] > [data-popper-arrow] {
4205
+ bottom: -5px;
4206
+ }
4207
+ [data-popover][role=tooltip][data-popper-placement^=bottom] > [data-popper-arrow] {
4208
+ top: -5px;
4209
+ }
4210
+ [data-popover][role=tooltip][data-popper-placement^=left] > [data-popper-arrow] {
4211
+ right: -5px;
4212
+ }
4213
+ [data-popover][role=tooltip][data-popper-placement^=right] > [data-popper-arrow] {
4214
+ left: -5px;
4215
+ }
3913
4216
  *,
3914
4217
  ::before,
3915
4218
  ::after {
@@ -3934,7 +4237,7 @@ video {
3934
4237
  --tw-ring-inset: ;
3935
4238
  --tw-ring-offset-width: 0px;
3936
4239
  --tw-ring-offset-color: #fff;
3937
- --tw-ring-color: rgb(59 130 246 / 0.5);
4240
+ --tw-ring-color: rgb(63 131 248 / 0.5);
3938
4241
  --tw-ring-offset-shadow: 0 0 #0000;
3939
4242
  --tw-ring-shadow: 0 0 #0000;
3940
4243
  --tw-shadow: 0 0 #0000;
@@ -3980,7 +4283,7 @@ video {
3980
4283
  --tw-ring-inset: ;
3981
4284
  --tw-ring-offset-width: 0px;
3982
4285
  --tw-ring-offset-color: #fff;
3983
- --tw-ring-color: rgb(59 130 246 / 0.5);
4286
+ --tw-ring-color: rgb(63 131 248 / 0.5);
3984
4287
  --tw-ring-offset-shadow: 0 0 #0000;
3985
4288
  --tw-ring-shadow: 0 0 #0000;
3986
4289
  --tw-shadow: 0 0 #0000;
@@ -4004,48 +4307,44 @@ video {
4004
4307
  --tw-backdrop-saturate: ;
4005
4308
  --tw-backdrop-sepia: ;
4006
4309
  }
4007
- .fixed {
4008
- position: fixed;
4009
- }
4010
- .left-20 {
4011
- left: 5rem;
4012
- }
4013
4310
  .z-10 {
4014
4311
  z-index: 10;
4015
4312
  }
4016
- .mt-2 {
4017
- margin-top: 0.5rem;
4018
- }
4019
4313
  .block {
4020
4314
  display: block;
4021
4315
  }
4022
4316
  .inline {
4023
4317
  display: inline;
4024
4318
  }
4025
- .w-56 {
4026
- width: 14rem;
4319
+ .hidden {
4320
+ display: none;
4027
4321
  }
4028
- .origin-top-right {
4029
- transform-origin: top right;
4322
+ .w-169 {
4323
+ width: 169px;
4030
4324
  }
4031
- .rounded-md {
4032
- border-radius: 6px;
4325
+ .divide-y > :not([hidden]) ~ :not([hidden]) {
4326
+ --tw-divide-y-reverse: 0;
4327
+ border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
4328
+ border-bottom-width: calc(1px * var(--tw-divide-y-reverse));
4329
+ }
4330
+ .divide-gray-100 > :not([hidden]) ~ :not([hidden]) {
4331
+ --tw-divide-opacity: 1;
4332
+ border-color: rgb(243 244 246 / var(--tw-divide-opacity));
4333
+ }
4334
+ .rounded-lg {
4335
+ border-radius: 0.5rem;
4033
4336
  }
4034
4337
  .bg-white {
4035
4338
  --tw-bg-opacity: 1;
4036
4339
  background-color: rgb(255 255 255 / var(--tw-bg-opacity));
4037
4340
  }
4038
- .py-1 {
4039
- padding-top: 0.25rem;
4040
- padding-bottom: 0.25rem;
4041
- }
4042
4341
  .italic {
4043
4342
  font-style: italic;
4044
4343
  }
4045
4344
  .underline {
4046
4345
  text-decoration-line: underline;
4047
4346
  }
4048
- .shadow-md {
4347
+ .shadow {
4049
4348
  --tw-shadow: 0 0 0 1px rgba(0,0,0,0.04), 0 1px 12px 4px rgba(0,0,0,0.12);
4050
4349
  --tw-shadow-colored: 0 0 0 1px var(--tw-shadow-color), 0 1px 12px 4px var(--tw-shadow-color);
4051
4350
  box-shadow:
@@ -4053,6 +4352,9 @@ video {
4053
4352
  var(--tw-ring-shadow, 0 0 #0000),
4054
4353
  var(--tw-shadow);
4055
4354
  }
4355
+ .formatted-text-editor {
4356
+ font-family: "Open Sans" !important;
4357
+ }
4056
4358
  .formatted-text-editor.editor-wrapper {
4057
4359
  border-radius: 4px;
4058
4360
  border-width: 2px;
@@ -4094,6 +4396,42 @@ video {
4094
4396
  var(--tw-ring-shadow, 0 0 #0000),
4095
4397
  var(--tw-shadow);
4096
4398
  }
4399
+ .remirror-theme h1 {
4400
+ font-size: 1.625rem;
4401
+ font-weight: 600;
4402
+ letter-spacing: -0.2px;
4403
+ line-height: 2rem;
4404
+ }
4405
+ .remirror-theme h2 {
4406
+ font-size: 1.25rem;
4407
+ font-weight: 600;
4408
+ letter-spacing: -0.5px;
4409
+ line-height: 1.5rem;
4410
+ }
4411
+ .remirror-theme h3 {
4412
+ font-size: 1.125rem;
4413
+ font-weight: 600;
4414
+ letter-spacing: -0.2px;
4415
+ line-height: 1.375rem;
4416
+ }
4417
+ .remirror-theme h4 {
4418
+ font-size: 1rem;
4419
+ font-weight: 700;
4420
+ letter-spacing: -0.2px;
4421
+ line-height: 1.25rem;
4422
+ }
4423
+ .remirror-theme h5 {
4424
+ font-size: 1rem;
4425
+ font-weight: 600;
4426
+ letter-spacing: -0.2px;
4427
+ line-height: 1.25rem;
4428
+ }
4429
+ .remirror-theme h6 {
4430
+ font-size: 0.875rem;
4431
+ font-weight: 600;
4432
+ letter-spacing: -0.2px;
4433
+ line-height: 1.25rem;
4434
+ }
4097
4435
  .formatted-text-editor .editor-toolbar {
4098
4436
  border-radius: 4px;
4099
4437
  border-width: 2px;
@@ -4172,10 +4510,7 @@ video {
4172
4510
  --tw-text-opacity: 1;
4173
4511
  color: rgb(7 116 210 / var(--tw-text-opacity));
4174
4512
  }
4175
- .toolbar-dropdown {
4176
- align-self: center;
4177
- }
4178
- .toolbar-dropdown .toolbar-dropdown__button {
4513
+ .toolbar-dropdown__button {
4179
4514
  font-family:
4180
4515
  Open Sans,
4181
4516
  Arial,
@@ -4184,25 +4519,34 @@ video {
4184
4519
  font-weight: 600;
4185
4520
  --tw-text-opacity: 1;
4186
4521
  color: rgb(112 112 112 / var(--tw-text-opacity));
4522
+ align-self: center;
4187
4523
  height: 2rem;
4188
4524
  padding-left: 0.5rem;
4189
4525
  }
4190
- .toolbar-dropdown .toolbar-dropdown__button:active,
4191
- .toolbar-dropdown .toolbar-dropdown__button:hover,
4192
- .toolbar-dropdown .toolbar-dropdown__button:focus {
4526
+ .toolbar-dropdown__button:active,
4527
+ .toolbar-dropdown__button:hover,
4528
+ .toolbar-dropdown__button:focus {
4193
4529
  background-color: rgba(0, 0, 0, 0.04);
4194
4530
  }
4195
- .toolbar-dropdown .toolbar-dropdown__button .toolbar-dropdown__label {
4196
- padding-right: 0.25rem;
4531
+ .toolbar-dropdown__button .toolbar-dropdown__label {
4532
+ display: inline-flex;
4533
+ width: 7rem;
4197
4534
  }
4198
- .toolbar-dropdown .toolbar-dropdown__button .toolbar-dropdown__icon {
4535
+ .toolbar-dropdown__button .toolbar-dropdown__icon {
4199
4536
  width: 1rem;
4200
4537
  height: 1.5rem;
4201
4538
  }
4539
+ .toolbar-dropdown__menu {
4540
+ inset: 0rem auto auto 1rem !important;
4541
+ }
4202
4542
  .dropdown-button {
4203
- height: 2.5rem;
4204
- padding: 0.25rem 0.5rem;
4205
- justify-content: space-between;
4206
- border-radius: 0;
4543
+ padding: 4px 8px;
4544
+ color: #707070;
4545
+ height: 40px;
4207
4546
  width: 100%;
4547
+ justify-content: space-between;
4548
+ }
4549
+ .dark .dark\:bg-gray-700 {
4550
+ --tw-bg-opacity: 1;
4551
+ background-color: rgb(55 65 81 / var(--tw-bg-opacity));
4208
4552
  }
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import CheckIcon from '@mui/icons-material/Check';
3
3
  const DropdownButton = ({ children, handleOnClick, isDisabled, isActive, label }) => {
4
4
  return (React.createElement("button", { "aria-label": label, title: label, type: "button", onClick: handleOnClick, disabled: isDisabled, className: `btn dropdown-button ${isActive ? 'is-active' : ''}` },
5
- children || label,
5
+ React.createElement("span", null, children || label),
6
6
  isActive && React.createElement(CheckIcon, { className: "dropdown-button-icon" })));
7
7
  };
8
8
  export default DropdownButton;
@@ -1,3 +1,4 @@
1
+ import 'flowbite';
1
2
  type ToolbarDropdownProps = {
2
3
  children: JSX.Element | JSX.Element[];
3
4
  label: string;
@@ -1,13 +1,12 @@
1
1
  import React from 'react';
2
- import { Menu } from '@headlessui/react';
3
2
  import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
3
+ import 'flowbite';
4
4
  const ToolbarDropdown = ({ children, label }) => {
5
- return (React.createElement(Menu, { as: "div", className: "toolbar-dropdown" },
6
- React.createElement("div", null,
7
- React.createElement(Menu.Button, { className: "toolbar-dropdown__button" },
8
- React.createElement("span", { className: "toolbar-dropdown__label" }, label),
9
- React.createElement(ExpandMoreIcon, { className: "toolbar-dropdown__icon", "aria-hidden": "true" }))),
10
- React.createElement(Menu.Items, { className: "fixed left-20 z-10 mt-2 w-56 origin-top-right rounded-md bg-white shadow-md " },
11
- React.createElement("div", { className: "py-1" }, children))));
5
+ return (React.createElement(React.Fragment, null,
6
+ React.createElement("button", { id: "dropdownHoverButton", "data-dropdown-toggle": "dropdown", className: "toolbar-dropdown__button", type: "button" },
7
+ React.createElement("span", { className: "toolbar-dropdown__label" }, label),
8
+ React.createElement(ExpandMoreIcon, { className: "toolbar-dropdown__icon", "aria-hidden": "true" })),
9
+ React.createElement("div", { id: "dropdown", className: "toolbar-dropdown__menu z-10 hidden bg-white divide-y divide-gray-100 rounded-lg shadow w-169 dark:bg-gray-700" },
10
+ React.createElement("ul", { "aria-labelledby": "dropdownHoverButton" }, children))));
12
11
  };
13
12
  export default ToolbarDropdown;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squiz/formatted-text-editor",
3
- "version": "1.12.0-alpha.38",
3
+ "version": "1.12.0-alpha.40",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "scripts": {
@@ -16,9 +16,9 @@
16
16
  "test:e2e": "vite build && vite preview --port 8080 & cypress open"
17
17
  },
18
18
  "dependencies": {
19
- "@headlessui/react": "^1.7.10",
20
19
  "@mui/icons-material": "5.11.0",
21
- "@remirror/react": "2.0.25"
20
+ "@remirror/react": "2.0.25",
21
+ "flowbite": "^1.6.3"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@testing-library/cypress": "9.0.0",
@@ -61,5 +61,5 @@
61
61
  "volta": {
62
62
  "node": "16.19.0"
63
63
  },
64
- "gitHead": "d4b60ab436f867e63c904ecabf780dce2a2b0a44"
64
+ "gitHead": "8db852ade2813dc297b7d0fa4975e0c5a57ac83e"
65
65
  }
@@ -0,0 +1,40 @@
1
+ import { Remirror, useRemirror, useRemirrorContext } from '@remirror/react';
2
+ import React from 'react';
3
+ import { EditorToolbar } from '../EditorToolbar/EditorToolbar';
4
+ import { Extensions } from '../Extensions/Extensions';
5
+
6
+ type MockEditorProps = {
7
+ setContent?: any;
8
+ };
9
+
10
+ export const MockEditor = ({ setContent }: MockEditorProps) => {
11
+ const { manager, state, setState } = useRemirror({
12
+ extensions: Extensions,
13
+ selection: 'start',
14
+ stringHandler: 'html',
15
+ });
16
+
17
+ const Component = () => {
18
+ setContent && setContent.mockImplementation(useRemirrorContext().setContent);
19
+ return null;
20
+ };
21
+
22
+ const handleChange = (parameter: { state: any }) => {
23
+ setState(parameter.state);
24
+ };
25
+
26
+ return (
27
+ <Remirror
28
+ manager={manager}
29
+ onChange={handleChange}
30
+ placeholder="Write something"
31
+ label="Text editor"
32
+ state={state}
33
+ autoRender="start"
34
+ >
35
+ <EditorToolbar manager={manager} />
36
+ <Component />
37
+ <EditorToolbar manager={manager} isPopover />
38
+ </Remirror>
39
+ );
40
+ };
@@ -1,7 +1,11 @@
1
+ import { jest } from '@jest/globals';
1
2
  import React from 'react';
2
- import '@testing-library/jest-dom';
3
+ import { act, fireEvent, render, screen } from '@testing-library/react';
4
+ import { MockEditor } from './Editor.mock';
3
5
  import Editor from './Editor';
4
- import { fireEvent, render, screen } from '@testing-library/react';
6
+ import '@testing-library/jest-dom';
7
+
8
+ const setContent: any = jest.fn();
5
9
 
6
10
  describe('Formatted text editor', () => {
7
11
  it('Renders the text editor', () => {
@@ -92,7 +96,7 @@ describe('Formatted text editor', () => {
92
96
 
93
97
  expect(baseElement.querySelector('div.remirror-editor h1')).toBeFalsy();
94
98
 
95
- const headingDropdown = baseElement.querySelector('.toolbar-dropdown button') as HTMLButtonElement;
99
+ const headingDropdown = baseElement.querySelector('.toolbar-dropdown__button') as HTMLButtonElement;
96
100
  expect(headingDropdown).toBeTruthy();
97
101
  fireEvent.click(headingDropdown);
98
102
 
@@ -109,7 +113,7 @@ describe('Formatted text editor', () => {
109
113
 
110
114
  expect(baseElement.querySelector('div.remirror-editor h2')).toBeFalsy();
111
115
 
112
- const headingDropdown = baseElement.querySelector('.toolbar-dropdown button') as HTMLButtonElement;
116
+ const headingDropdown = baseElement.querySelector('.toolbar-dropdown__button') as HTMLButtonElement;
113
117
  expect(headingDropdown).toBeTruthy();
114
118
  fireEvent.click(headingDropdown);
115
119
 
@@ -126,7 +130,7 @@ describe('Formatted text editor', () => {
126
130
 
127
131
  expect(baseElement.querySelector('div.remirror-editor h3')).toBeFalsy();
128
132
 
129
- const headingDropdown = baseElement.querySelector('.toolbar-dropdown button') as HTMLButtonElement;
133
+ const headingDropdown = baseElement.querySelector('.toolbar-dropdown__button') as HTMLButtonElement;
130
134
  expect(headingDropdown).toBeTruthy();
131
135
  fireEvent.click(headingDropdown);
132
136
 
@@ -143,7 +147,7 @@ describe('Formatted text editor', () => {
143
147
 
144
148
  expect(baseElement.querySelector('div.remirror-editor h4')).toBeFalsy();
145
149
 
146
- const headingDropdown = baseElement.querySelector('.toolbar-dropdown button') as HTMLButtonElement;
150
+ const headingDropdown = baseElement.querySelector('.toolbar-dropdown__button') as HTMLButtonElement;
147
151
  expect(headingDropdown).toBeTruthy();
148
152
  fireEvent.click(headingDropdown);
149
153
 
@@ -160,7 +164,7 @@ describe('Formatted text editor', () => {
160
164
 
161
165
  expect(baseElement.querySelector('div.remirror-editor h5')).toBeFalsy();
162
166
 
163
- const headingDropdown = baseElement.querySelector('.toolbar-dropdown button') as HTMLButtonElement;
167
+ const headingDropdown = baseElement.querySelector('.toolbar-dropdown__button') as HTMLButtonElement;
164
168
  expect(headingDropdown).toBeTruthy();
165
169
  fireEvent.click(headingDropdown);
166
170
 
@@ -177,7 +181,7 @@ describe('Formatted text editor', () => {
177
181
 
178
182
  expect(baseElement.querySelector('div.remirror-editor h6')).toBeFalsy();
179
183
 
180
- const headingDropdown = baseElement.querySelector('.toolbar-dropdown button') as HTMLButtonElement;
184
+ const headingDropdown = baseElement.querySelector('.toolbar-dropdown__button') as HTMLButtonElement;
181
185
  expect(headingDropdown).toBeTruthy();
182
186
  fireEvent.click(headingDropdown);
183
187
 
@@ -194,7 +198,7 @@ describe('Formatted text editor', () => {
194
198
 
195
199
  expect(baseElement.querySelector('div.remirror-editor pre')).toBeFalsy();
196
200
 
197
- const headingDropdown = baseElement.querySelector('.toolbar-dropdown button') as HTMLButtonElement;
201
+ const headingDropdown = baseElement.querySelector('.toolbar-dropdown__button') as HTMLButtonElement;
198
202
  expect(headingDropdown).toBeTruthy();
199
203
  fireEvent.click(headingDropdown);
200
204
 
@@ -211,7 +215,7 @@ describe('Formatted text editor', () => {
211
215
 
212
216
  expect(baseElement.querySelectorAll('div.remirror-editor p')).toHaveLength(1);
213
217
 
214
- const headingDropdown = baseElement.querySelector('.toolbar-dropdown button') as HTMLButtonElement;
218
+ const headingDropdown = baseElement.querySelector('.toolbar-dropdown__button') as HTMLButtonElement;
215
219
  expect(headingDropdown).toBeTruthy();
216
220
  fireEvent.click(headingDropdown);
217
221
 
@@ -225,4 +229,26 @@ describe('Formatted text editor', () => {
225
229
 
226
230
  expect(baseElement.querySelectorAll('div.remirror-editor p')).toHaveLength(2);
227
231
  });
232
+
233
+ it('Should allow text input & undo input upon clicking undo button', () => {
234
+ const { baseElement, getByLabelText } = render(<MockEditor setContent={setContent} />);
235
+
236
+ const textContent = `This is a string with a random number: ${Math.random() * 9999}`;
237
+
238
+ // This sets the content of the text editor
239
+ act(() => {
240
+ setContent(`<p>${textContent}</p>`, { triggerChange: true });
241
+ });
242
+
243
+ const editorNode = getByLabelText(`Text editor`);
244
+ expect(editorNode).toBeTruthy();
245
+ expect(editorNode.textContent).toBe(textContent);
246
+
247
+ // Testing if clicking undo button removes text from editor
248
+ const undoButton = baseElement.querySelector('button[title="Undo (cmd+Z)"]') as HTMLButtonElement;
249
+ expect(undoButton).toBeTruthy();
250
+ fireEvent.click(undoButton);
251
+
252
+ expect(editorNode.textContent).toBe('');
253
+ });
228
254
  });
@@ -1,18 +1,8 @@
1
1
  import React from 'react';
2
- import {
3
- BoldExtension,
4
- HeadingExtension,
5
- ItalicExtension,
6
- NodeFormattingExtension,
7
- ParagraphExtension,
8
- UnderlineExtension,
9
- HistoryExtension,
10
- wysiwygPreset,
11
- } from 'remirror/extensions';
12
2
  import { EditorComponent, Remirror, useRemirror } from '@remirror/react';
13
- import { RemirrorContentType, Extension } from '@remirror/core';
3
+ import { RemirrorContentType } from '@remirror/core';
14
4
  import { EditorToolbar } from '../EditorToolbar/EditorToolbar';
15
- import { PreformattedExtension } from '../extensions/PreformattedExtension/PreformattedExtension';
5
+ import { Extensions } from '../Extensions/Extensions';
16
6
 
17
7
  type EditorProps = {
18
8
  content?: RemirrorContentType;
@@ -20,17 +10,7 @@ type EditorProps = {
20
10
 
21
11
  const Editor = ({ content }: EditorProps) => {
22
12
  const { manager, state, setState } = useRemirror({
23
- extensions: () => [
24
- ...(wysiwygPreset() as Extension[]),
25
- new BoldExtension(),
26
- new HeadingExtension(),
27
- new ItalicExtension(),
28
- new NodeFormattingExtension(),
29
- new ParagraphExtension(),
30
- new PreformattedExtension(),
31
- new UnderlineExtension(),
32
- new HistoryExtension(),
33
- ],
13
+ extensions: Extensions,
34
14
  content,
35
15
  selection: 'start',
36
16
  stringHandler: 'html',
@@ -1,4 +1,6 @@
1
1
  .formatted-text-editor {
2
+ font-family: 'Open Sans' !important;
3
+
2
4
  &.editor-wrapper {
3
5
  @apply bg-white rounded border-gray-300 border-2 border-solid;
4
6
  }
@@ -29,3 +31,45 @@
29
31
  @apply shadow-none;
30
32
  }
31
33
  }
34
+
35
+ .remirror-theme h1 {
36
+ font-size: 1.625rem;
37
+ font-weight: 600;
38
+ letter-spacing: -0.2px;
39
+ line-height: 2rem;
40
+ }
41
+
42
+ .remirror-theme h2 {
43
+ font-size: 1.25rem;
44
+ font-weight: 600;
45
+ letter-spacing: -0.5px;
46
+ line-height: 1.5rem;
47
+ }
48
+
49
+ .remirror-theme h3 {
50
+ font-size: 1.125rem;
51
+ font-weight: 600;
52
+ letter-spacing: -0.2px;
53
+ line-height: 1.375rem;
54
+ }
55
+
56
+ .remirror-theme h4 {
57
+ font-size: 1rem;
58
+ font-weight: 700;
59
+ letter-spacing: -0.2px;
60
+ line-height: 1.25rem;
61
+ }
62
+
63
+ .remirror-theme h5 {
64
+ font-size: 1rem;
65
+ font-weight: 600;
66
+ letter-spacing: -0.2px;
67
+ line-height: 1.25rem;
68
+ }
69
+
70
+ .remirror-theme h6 {
71
+ font-size: 0.875rem;
72
+ font-weight: 600;
73
+ letter-spacing: -0.2px;
74
+ line-height: 1.25rem;
75
+ }
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { useCommands, useActive } from '@remirror/react';
3
- import { PreformattedExtension } from '../../../../extensions/PreformattedExtension/PreformattedExtension';
3
+ import { PreformattedExtension } from '../../../../Extensions/PreformattedExtension/PreformattedExtension';
4
4
  import DropdownButton from '../../../../ui/DropdownButton/DropdownButton';
5
5
 
6
6
  const PreformattedButton = () => {
@@ -4,7 +4,7 @@ import ParagraphButton from './Paragraph/ParagraphButton';
4
4
  import PreformattedButton from './Preformatted/PreformattedButton';
5
5
  import ToolbarDropdown from '../../../ui/ToolbarDropdown/ToolbarDropdown';
6
6
  import { useActive, VerticalDivider } from '@remirror/react';
7
- import { PreformattedExtension } from '../../../extensions/PreformattedExtension/PreformattedExtension';
7
+ import { PreformattedExtension } from '../../../Extensions/PreformattedExtension/PreformattedExtension';
8
8
 
9
9
  const TextTypeDropdown = () => {
10
10
  const active = useActive<PreformattedExtension>();
@@ -0,0 +1,24 @@
1
+ import {
2
+ BoldExtension,
3
+ HeadingExtension,
4
+ ItalicExtension,
5
+ NodeFormattingExtension,
6
+ ParagraphExtension,
7
+ UnderlineExtension,
8
+ HistoryExtension,
9
+ wysiwygPreset,
10
+ } from 'remirror/extensions';
11
+ import { PreformattedExtension } from './PreformattedExtension/PreformattedExtension';
12
+ import { Extension } from '@remirror/core';
13
+
14
+ export const Extensions = () => [
15
+ ...(wysiwygPreset() as Extension[]),
16
+ new BoldExtension(),
17
+ new HeadingExtension(),
18
+ new ItalicExtension(),
19
+ new NodeFormattingExtension(),
20
+ new ParagraphExtension(),
21
+ new PreformattedExtension(),
22
+ new UnderlineExtension(),
23
+ new HistoryExtension(),
24
+ ];
@@ -0,0 +1,10 @@
1
+ import { render } from '@testing-library/react';
2
+ import { FormattedTextEditor } from './';
3
+ import React from 'react';
4
+
5
+ describe('<FormattedTextEditor />', () => {
6
+ it('should render "<FormattedTextEditor />" component', () => {
7
+ const { baseElement } = render(<FormattedTextEditor />);
8
+ expect(baseElement).toBeTruthy();
9
+ });
10
+ });
@@ -19,7 +19,7 @@ const DropdownButton = ({ children, handleOnClick, isDisabled, isActive, label }
19
19
  disabled={isDisabled}
20
20
  className={`btn dropdown-button ${isActive ? 'is-active' : ''}`}
21
21
  >
22
- {children || label}
22
+ <span>{children || label}</span>
23
23
  {isActive && <CheckIcon className="dropdown-button-icon" />}
24
24
  </button>
25
25
  );
@@ -1,7 +1,7 @@
1
1
  .dropdown-button {
2
- height: 2.5rem;
3
- padding: 0.25rem 0.5rem;
4
- justify-content: space-between;
5
- border-radius: 0;
2
+ padding: 4px 8px;
3
+ color: #707070;
4
+ height: 40px;
6
5
  width: 100%;
6
+ justify-content: space-between;
7
7
  }
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { Menu } from '@headlessui/react';
3
2
  import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
3
+ import 'flowbite';
4
4
 
5
5
  type ToolbarDropdownProps = {
6
6
  children: JSX.Element | JSX.Element[];
@@ -9,18 +9,23 @@ type ToolbarDropdownProps = {
9
9
 
10
10
  const ToolbarDropdown = ({ children, label }: ToolbarDropdownProps) => {
11
11
  return (
12
- <Menu as="div" className="toolbar-dropdown">
13
- <div>
14
- <Menu.Button className="toolbar-dropdown__button">
15
- <span className="toolbar-dropdown__label">{label}</span>
16
- <ExpandMoreIcon className="toolbar-dropdown__icon" aria-hidden="true" />
17
- </Menu.Button>
12
+ <>
13
+ <button
14
+ id="dropdownHoverButton"
15
+ data-dropdown-toggle="dropdown"
16
+ className="toolbar-dropdown__button"
17
+ type="button"
18
+ >
19
+ <span className="toolbar-dropdown__label">{label}</span>
20
+ <ExpandMoreIcon className="toolbar-dropdown__icon" aria-hidden="true" />
21
+ </button>
22
+ <div
23
+ id="dropdown"
24
+ className="toolbar-dropdown__menu z-10 hidden bg-white divide-y divide-gray-100 rounded-lg shadow w-169 dark:bg-gray-700"
25
+ >
26
+ <ul aria-labelledby="dropdownHoverButton">{children}</ul>
18
27
  </div>
19
-
20
- <Menu.Items className="fixed left-20 z-10 mt-2 w-56 origin-top-right rounded-md bg-white shadow-md ">
21
- <div className="py-1">{children}</div>
22
- </Menu.Items>
23
- </Menu>
28
+ </>
24
29
  );
25
30
  };
26
31
 
@@ -1,25 +1,27 @@
1
- .toolbar-dropdown {
1
+ .toolbar-dropdown__button {
2
+ @apply font-base text-md font-semibold text-gray-600;
2
3
  align-self: center;
3
4
 
4
- .toolbar-dropdown__button {
5
- @apply font-base text-md font-semibold text-gray-600;
5
+ height: 2rem;
6
+ padding-left: 0.5rem;
6
7
 
7
- height: 2rem;
8
- padding-left: 0.5rem;
9
-
10
- &:active,
11
- &:hover,
12
- &:focus {
13
- background-color: rgba(black, 0.04);
14
- }
8
+ &:active,
9
+ &:hover,
10
+ &:focus {
11
+ background-color: rgba(black, 0.04);
12
+ }
15
13
 
16
- .toolbar-dropdown__label {
17
- padding-right: 0.25rem;
18
- }
14
+ .toolbar-dropdown__label {
15
+ display: inline-flex;
16
+ width: 7rem;
17
+ }
19
18
 
20
- .toolbar-dropdown__icon {
21
- width: 1rem;
22
- height: 1.5rem;
23
- }
19
+ .toolbar-dropdown__icon {
20
+ width: 1rem;
21
+ height: 1.5rem;
24
22
  }
25
23
  }
24
+
25
+ .toolbar-dropdown__menu {
26
+ inset: 0rem auto auto 1rem !important;
27
+ }
@@ -1,6 +1,6 @@
1
1
  /** @type {import('tailwindcss').Config} */
2
2
  module.exports = {
3
- content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
3
+ content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}', './node_modules/flowbite/**/*.js'],
4
4
  theme: {
5
5
  extend: {
6
6
  borderRadius: {
@@ -40,6 +40,7 @@ module.exports = {
40
40
  6: '1.5rem', // 24px
41
41
  7: '1.75rem', // 28px
42
42
  8: '2rem', // 32px
43
+ 169: '169px', // 169px
43
44
  },
44
45
  colors: {
45
46
  gray: {
@@ -57,5 +58,5 @@ module.exports = {
57
58
  },
58
59
  },
59
60
  },
60
- plugins: [],
61
+ plugins: [require('flowbite/plugin')],
61
62
  };