Skip to content

Commit dd191a4

Browse files
author
Morten Barklund
authored
Inline text formatting (#1323)
* WIP: First partially working version of inline text formatting * Added fix for importing elements with multiple styles * Added the option to toggle b/i/u for entire text field * Cleaned up the structure a bit * Added support for font weight and multi-select * Update tests now that some values are not in use * Adding `npx` to postinstall to see if CI approves * Removed `postinstall-postinstall` * If selection is collapsed when applying style change, apply to inline override * Reorganised selection manip and added letter spacing * Disable force focus when setting letter spacing Actually the whole `forceFocus` variable was superfluous - `EditorState.forceSelection` autimatically forces focus to the editor. And selection was remembered, just not visible while unfocused, so things still work as expected. * Added inline text color formatting * Fix multiple reducer for patterns, and fix tests * Fixed font and font weight dropdowns Font weight dropdown simply gets a new placeholder, as the placeholder will only be shown, when there's multiple font weights selected. For now, font weight logic is removed from font selector (but must be readded in a new version later), and tests are updated. * Remove deprecated types from text element * Some extra cleanup of deprecated properties * Added new migration with test (also added `jest-extended`) * Refactored formatters to verticals rather than horizontals - much cleaner result * Fixed reducer and added comments * Added jsdocs to html manipulation * Removed classes from migration and added test for nested tags * Fix tests for text style now that export does not have classes anymore * Moved focus management to edit component * Create general `getValidHTML` util * Added deps to `useImperativeHandle` * Fixed `parseFloat` args * Added faux-selection style and fixed various bugs * Fixed typo * For now, strip formatting when pasting * Remove weird npm scripts * Using a less nice matcher * Added tests for formatters * Set default font color to black - all other colors are fixed inline * Added unit tests for style manipulation These are slightly integrated with draft-js' editor state, as it was too complex too mock and this is actually a better test IMHO. * Fixed color input (allow multiple value string) * Fixed color picker to default to solid black (rather than transparent black) * Fixed null value for input * Fix color preset to work for new ITF formatting * Added full support for text color and text style presets * Inlined font weights for text panel presets * Fix focus out to check click target in capture phase * Re-add default props to test * Added jsdoc for draftUtils * Fix children proptype * Add better error handlign to faux selection removal * Added jsdocs for complex style manipulation functions * Added function to strip relevant inline styling * Added new function to compare patterns and used where applicable * Added ignore black to set color style as it is default * Changed highlight to use existing style manipulation Reverted changes to `getValidHTML` and reused the existing HTML manipulation functions. * Removed conditional hook
1 parent 8c7421a commit dd191a4

68 files changed

Lines changed: 4352 additions & 682 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/continuous-integration.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171
- name: Setup Node
7272
uses: actions/setup-node@v1
7373
with:
74-
node-version: "${{ steps.nvm.outputs.NVMRC }}"
74+
node-version: '${{ steps.nvm.outputs.NVMRC }}'
7575

7676
- name: Setup npm cache
7777
uses: pat-s/always-upload-cache@v1.1.4
@@ -96,8 +96,8 @@ jobs:
9696
- name: Annotate Code Linting Results
9797
uses: ataylorme/eslint-annotate-action@1.0.4
9898
with:
99-
repo-token: "${{ secrets.GITHUB_TOKEN }}"
100-
report-json: "build/lint-js-report.json"
99+
repo-token: '${{ secrets.GITHUB_TOKEN }}'
100+
report-json: 'build/lint-js-report.json'
101101

102102
markdownlint:
103103
name: Markdown Code Style
@@ -113,7 +113,7 @@ jobs:
113113
- name: Setup Node
114114
uses: actions/setup-node@v1
115115
with:
116-
node-version: "${{ steps.nvm.outputs.NVMRC }}"
116+
node-version: '${{ steps.nvm.outputs.NVMRC }}'
117117

118118
- name: Setup npm cache
119119
uses: pat-s/always-upload-cache@v1.1.4
@@ -148,7 +148,7 @@ jobs:
148148
- name: Setup Node
149149
uses: actions/setup-node@v1
150150
with:
151-
node-version: "${{ steps.nvm.outputs.NVMRC }}"
151+
node-version: '${{ steps.nvm.outputs.NVMRC }}'
152152

153153
- name: Setup npm cache
154154
uses: pat-s/always-upload-cache@v1.1.4
@@ -328,7 +328,7 @@ jobs:
328328
- name: Setup Node
329329
uses: actions/setup-node@v1
330330
with:
331-
node-version: "${{ steps.nvm.outputs.NVMRC }}"
331+
node-version: '${{ steps.nvm.outputs.NVMRC }}'
332332

333333
- name: Setup npm cache
334334
uses: pat-s/always-upload-cache@v1.1.4
@@ -405,7 +405,7 @@ jobs:
405405
- name: Setup Node
406406
uses: actions/setup-node@v1
407407
with:
408-
node-version: "${{ steps.nvm.outputs.NVMRC }}"
408+
node-version: '${{ steps.nvm.outputs.NVMRC }}'
409409

410410
- name: Setup npm cache
411411
uses: pat-s/always-upload-cache@v1.1.4

.github/workflows/release-assets.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
- name: Setup Node
2222
uses: actions/setup-node@v1
2323
with:
24-
node-version: "${{ steps.nvm.outputs.NVMRC }}"
24+
node-version: '${{ steps.nvm.outputs.NVMRC }}'
2525

2626
- name: Setup npm cache
2727
uses: pat-s/always-upload-cache@v1.1.4

assets/src/edit-story/components/colorPicker/colorPicker.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { __ } from '@wordpress/i18n';
3333
import { PatternPropType } from '../../types';
3434
import { useKeyDownEffect } from '../keyboard';
3535
import useFocusOut from '../../utils/useFocusOut';
36+
import createSolid from '../../utils/createSolid';
3637
import CurrentColorPicker from './currentColorPicker';
3738
import GradientPicker from './gradientPicker';
3839
import Header from './header';
@@ -102,6 +103,9 @@ function ColorPicker({
102103
useEffect(() => {
103104
if (color) {
104105
load(color);
106+
} else {
107+
// If no color given, load solid black
108+
load(createSolid(0, 0, 0));
105109
}
106110
}, [color, load]);
107111

assets/src/edit-story/components/form/color/color.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ function ColorInput({
6464
}
6565

6666
ColorInput.propTypes = {
67-
value: PatternPropType,
67+
value: PropTypes.oneOfType([PatternPropType, PropTypes.string]),
6868
hasGradient: PropTypes.bool,
6969
hasOpacity: PropTypes.bool,
7070
onChange: PropTypes.func.isRequired,

assets/src/edit-story/components/form/color/colorPreview.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ function ColorPreview({
172172
<VisualPreview role="status" style={previewStyle} {...buttonProps} />
173173
<TextualInput
174174
aria-label={`${inputLabel}: ${label}`}
175-
value={hexInputValue}
175+
value={hexInputValue ?? ''}
176176
onChange={handleInputChange}
177177
onBlur={handleInputBlur}
178178
/>

assets/src/edit-story/components/form/color/opacityPreview.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { _x, __ } from '@wordpress/i18n';
3131
*/
3232
import { PatternPropType } from '../../../types';
3333
import useFocusAndSelect from '../../../utils/useFocusAndSelect';
34+
import { MULTIPLE_VALUE } from '../';
3435
import getPreviewText from './getPreviewText';
3536
import getPreviewOpacity from './getPreviewOpacity';
3637
import ColorBox from './colorBox';
@@ -47,7 +48,8 @@ const Input = styled(ColorBox).attrs({
4748
`;
4849

4950
function OpacityPreview({ value, onChange }) {
50-
const hasPreviewText = Boolean(getPreviewText(value));
51+
const hasPreviewText =
52+
value !== MULTIPLE_VALUE && Boolean(getPreviewText(value));
5153
const postfix = _x('%', 'Percentage', 'web-stories');
5254
const [inputValue, setInputValue] = useState('');
5355
const ref = useRef();
@@ -91,7 +93,7 @@ function OpacityPreview({ value, onChange }) {
9193
}
9294

9395
OpacityPreview.propTypes = {
94-
value: PatternPropType,
96+
value: PropTypes.oneOfType([PatternPropType, PropTypes.string]),
9597
onChange: PropTypes.func.isRequired,
9698
};
9799

assets/src/edit-story/components/library/panes/text/textPane.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@ const PRESETS = [
4242
{
4343
id: 'heading',
4444
title: __('Heading', 'web-stories'),
45-
content: __('Heading', 'web-stories'),
45+
content: `<span style="font-weight: 700">${__(
46+
'Heading',
47+
'web-stories'
48+
)}</span>`,
4649
fontSize: dataFontEm(2),
47-
fontWeight: 700,
4850
font: {
4951
family: 'Open Sans',
5052
service: 'fonts.google.com',
@@ -53,9 +55,11 @@ const PRESETS = [
5355
{
5456
id: 'subheading',
5557
title: __('Subheading', 'web-stories'),
56-
content: __('Subheading', 'web-stories'),
58+
content: `<span style="font-weight: 600">${__(
59+
'Subheading',
60+
'web-stories'
61+
)}</span>`,
5762
fontSize: dataFontEm(1.5),
58-
fontWeight: 600,
5963
font: {
6064
family: 'Open Sans',
6165
service: 'fonts.google.com',
@@ -69,7 +73,6 @@ const PRESETS = [
6973
'web-stories'
7074
),
7175
fontSize: dataFontEm(1.1),
72-
fontWeight: 400,
7376
font: {
7477
family: 'Roboto',
7578
service: 'fonts.google.com',

assets/src/edit-story/components/panels/stylePreset/panel.js

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,20 @@
1717
/**
1818
* External dependencies
1919
*/
20-
import { useCallback, useEffect, useState } from 'react';
20+
import { useCallback, useRef, useEffect, useState } from 'react';
2121

2222
/**
2323
* Internal dependencies
2424
*/
2525
import { useStory } from '../../../app/story';
2626
import stripHTML from '../../../utils/stripHTML';
27+
import objectWithout from '../../../utils/objectWithout';
28+
import { Panel } from '../panel';
29+
import useRichTextFormatting from '../textStyle/useRichTextFormatting';
2730
import {
2831
COLOR_PRESETS_PER_ROW,
2932
STYLE_PRESETS_PER_ROW,
3033
} from '../../../constants';
31-
import { Panel } from './../panel';
3234
import { getShapePresets, getTextPresets } from './utils';
3335
import PresetsHeader from './header';
3436
import Presets from './presets';
@@ -123,24 +125,46 @@ function StylePresetPanel() {
123125
]
124126
);
125127

128+
const extraPropsToAdd = useRef(null);
129+
const miniPushUpdate = useCallback(
130+
(updater) => {
131+
updateElementsById({
132+
elementIds: selectedElementIds,
133+
properties: (oldProps) => ({
134+
...updater(oldProps),
135+
...extraPropsToAdd.current,
136+
}),
137+
});
138+
extraPropsToAdd.current = null;
139+
},
140+
[selectedElementIds, updateElementsById]
141+
);
142+
143+
const {
144+
handlers: { handleSetColor },
145+
} = useRichTextFormatting(selectedElements, miniPushUpdate);
146+
126147
const handleApplyPreset = useCallback(
127148
(preset) => {
128149
if (isText) {
129150
// @todo Determine this in a better way.
130151
// Only style presets have background text mode set.
131152
const isStylePreset = preset.backgroundTextMode !== undefined;
132-
updateElementsById({
133-
elementIds: selectedElementIds,
134-
properties: isStylePreset ? { ...preset } : { color: preset },
135-
});
153+
if (isStylePreset) {
154+
extraPropsToAdd.current = objectWithout(preset, ['color']);
155+
handleSetColor(preset.color);
156+
} else {
157+
extraPropsToAdd.current = null;
158+
handleSetColor(preset);
159+
}
136160
} else {
137161
updateElementsById({
138162
elementIds: selectedElementIds,
139163
properties: { backgroundColor: preset },
140164
});
141165
}
142166
},
143-
[isText, selectedElementIds, updateElementsById]
167+
[isText, handleSetColor, selectedElementIds, updateElementsById]
144168
);
145169

146170
const colorPresets = isText ? textColors : fillColors;

assets/src/edit-story/components/panels/stylePreset/test/panel.js

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { BACKGROUND_TEXT_MODE } from '../../../../constants';
2828
import { getShapePresets, getTextPresets } from '../utils';
2929
import { renderWithTheme } from '../../../../testUtils';
3030
import { TEXT_ELEMENT_DEFAULT_FONT } from '../../../../app/font/defaultFonts';
31+
3132
jest.mock('../utils');
3233

3334
function setupPanel(extraStylePresets, extraStateProps) {
@@ -91,7 +92,6 @@ describe('Panels/StylePreset', () => {
9192
},
9293
};
9394
const STYLE_PRESET = {
94-
color: TEST_COLOR_2,
9595
backgroundTextMode: BACKGROUND_TEXT_MODE.FILL,
9696
backgroundColor: TEST_COLOR,
9797
};
@@ -149,13 +149,13 @@ describe('Panels/StylePreset', () => {
149149
expect(newEditButton).toBeDefined();
150150
});
151151

152-
it('should add a text color preset', () => {
152+
it('should add a text color preset if other text styles are default or missing', () => {
153153
const extraStateProps = {
154154
selectedElements: [
155155
{
156156
id: '1',
157157
type: 'text',
158-
color: [TEST_COLOR_2],
158+
content: '<span style="color: rgba(2, 2, 2)">Content</span>',
159159
backgroundTextMode: BACKGROUND_TEXT_MODE.NONE,
160160
font: TEXT_ELEMENT_DEFAULT_FONT,
161161
},
@@ -193,6 +193,7 @@ describe('Panels/StylePreset', () => {
193193
{
194194
id: '1',
195195
type: 'text',
196+
content: '<span style="color: rgba(2, 2, 2)">Content</span>',
196197
...STYLE_PRESET,
197198
},
198199
],
@@ -204,7 +205,7 @@ describe('Panels/StylePreset', () => {
204205

205206
getTextPresets.mockImplementation(() => {
206207
return {
207-
textStyles: [STYLE_PRESET],
208+
textStyles: [{ color: TEST_COLOR_2, ...STYLE_PRESET }],
208209
};
209210
});
210211

@@ -217,7 +218,7 @@ describe('Panels/StylePreset', () => {
217218
stylePresets: {
218219
textColors: [],
219220
fillColors: [],
220-
textStyles: [STYLE_PRESET],
221+
textStyles: [{ color: TEST_COLOR_2, ...STYLE_PRESET }],
221222
},
222223
},
223224
});
@@ -365,10 +366,17 @@ describe('Panels/StylePreset', () => {
365366
expect(updateElementsById).toHaveBeenCalledTimes(1);
366367
expect(updateElementsById).toHaveBeenCalledWith({
367368
elementIds: ['1'],
368-
properties: {
369-
color: TEST_COLOR,
370-
},
369+
properties: expect.any(Function),
371370
});
371+
const updaterFunction = updateElementsById.mock.calls[0][0].properties;
372+
const partiallyBlueContent = {
373+
content: 'Hello <span style="color: blue">World</span>',
374+
};
375+
const updatedContent = updaterFunction(partiallyBlueContent);
376+
const expectedContent = {
377+
content: '<span style="color: #010101">Hello World</span>',
378+
};
379+
expect(updatedContent).toStrictEqual(expectedContent);
372380
});
373381
});
374382
});

0 commit comments

Comments
 (0)