Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inline text formatting #1323

Merged
merged 63 commits into from May 6, 2020
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
536ac78
WIP: First partially working version of inline text formatting
Apr 23, 2020
2e023e8
Merge branch 'master' into feature/inline-rich-text
Apr 23, 2020
854510f
Added fix for importing elements with multiple styles
Apr 23, 2020
6fdac4c
Added the option to toggle b/i/u for entire text field
Apr 23, 2020
e0506bf
Cleaned up the structure a bit
Apr 23, 2020
323ec6c
Added support for font weight and multi-select
Apr 24, 2020
6377f5a
Update tests now that some values are not in use
Apr 24, 2020
48c765d
Adding `npx` to postinstall to see if CI approves
Apr 24, 2020
b0ba301
Removed `postinstall-postinstall`
Apr 24, 2020
1656c0a
If selection is collapsed when applying style change, apply to inline…
Apr 24, 2020
ad34f87
Reorganised selection manip and added letter spacing
Apr 24, 2020
4a28da7
Disable force focus when setting letter spacing
Apr 24, 2020
6346fa2
Added inline text color formatting
Apr 24, 2020
ebf8f26
Fix multiple reducer for patterns, and fix tests
Apr 24, 2020
04af4f5
Fixed font and font weight dropdowns
Apr 24, 2020
e17c4f6
Remove deprecated types from text element
Apr 24, 2020
47df628
Some extra cleanup of deprecated properties
Apr 24, 2020
5dcaf4e
Added new migration with test (also added `jest-extended`)
Apr 27, 2020
0f49ebd
Merge branch 'master' into feature/inline-rich-text
Apr 27, 2020
e8b1f4e
Merge branch 'master' into feature/inline-rich-text
Apr 28, 2020
9739dfb
Refactored formatters to verticals rather than horizontals - much cle…
Apr 29, 2020
41e6954
Fixed reducer and added comments
Apr 29, 2020
97a91cf
Added jsdocs to html manipulation
Apr 29, 2020
c4f4c18
Removed classes from migration and added test for nested tags
Apr 29, 2020
057f33c
Fix tests for text style now that export does not have classes anymore
Apr 29, 2020
8097c3f
Moved focus management to edit component
Apr 29, 2020
8685d11
Create general `getValidHTML` util
Apr 29, 2020
3a80904
Added deps to `useImperativeHandle`
Apr 29, 2020
e2d2f23
Fixed `parseFloat` args
Apr 29, 2020
4f58366
Merge branch 'master' into feature/inline-rich-text
Apr 29, 2020
a0b3259
Added faux-selection style and fixed various bugs
Apr 29, 2020
59134bc
Fixed typo
Apr 29, 2020
ab85df5
For now, strip formatting when pasting
Apr 29, 2020
37c9433
Merge branch 'master' into feature/inline-rich-text
Apr 29, 2020
2f09bc6
Remove weird npm scripts
Apr 29, 2020
7e2ed36
Using a less nice matcher
Apr 29, 2020
102c5af
Added tests for formatters
Apr 30, 2020
4f88010
Merge branch 'master' into feature/inline-rich-text
Apr 30, 2020
aa2fff6
Merge branch 'master' into feature/inline-rich-text
Apr 30, 2020
44d7a77
Set default font color to black - all other colors are fixed inline
Apr 30, 2020
b5a0c76
Added unit tests for style manipulation
May 1, 2020
080251b
Merge branch 'master' into feature/inline-rich-text
May 1, 2020
85edb58
Fixed color input (allow multiple value string)
May 1, 2020
e8f6266
Fixed color picker to default to solid black (rather than transparent…
May 1, 2020
f1dcb73
Fixed null value for input
May 1, 2020
03aa64b
Fix color preset to work for new ITF formatting
May 1, 2020
1b4f9de
Added full support for text color and text style presets
May 1, 2020
f0dd127
Inlined font weights for text panel presets
May 1, 2020
d411b08
Fix focus out to check click target in capture phase
May 1, 2020
f1c8468
Merge branch 'master' into feature/inline-rich-text
May 4, 2020
16dbf27
Re-add default props to test
May 4, 2020
e295b37
Added jsdoc for draftUtils
May 4, 2020
870f2bd
Fix children proptype
May 4, 2020
e03baf3
Add better error handlign to faux selection removal
May 4, 2020
0081a79
Added jsdocs for complex style manipulation functions
May 4, 2020
c5adfec
Added function to strip relevant inline styling
May 4, 2020
cd77cb9
Merge branch 'master' into feature/inline-rich-text
May 5, 2020
7a28375
Merge branch 'master' into feature/inline-rich-text
May 5, 2020
ba711d7
Added new function to compare patterns and used where applicable
May 5, 2020
b63842a
Added ignore black to set color style as it is default
May 6, 2020
b6468b0
Changed highlight to use existing style manipulation
May 6, 2020
595d4dd
Merge branch 'master' into feature/inline-rich-text
May 6, 2020
13b6fda
Removed conditional hook
May 6, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/continuous-deployment.yml
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v1
with:
node-version: "${{ steps.nvm.outputs.NVMRC }}"
node-version: '${{ steps.nvm.outputs.NVMRC }}'

- name: Setup npm cache
uses: pat-s/always-upload-cache@v1.1.4
Expand Down
4 changes: 4 additions & 0 deletions assets/src/edit-story/components/colorPicker/colorPicker.js
Expand Up @@ -33,6 +33,7 @@ import { __ } from '@wordpress/i18n';
import { PatternPropType } from '../../types';
import { useKeyDownEffect } from '../keyboard';
import useFocusOut from '../../utils/useFocusOut';
import createSolid from '../../utils/createSolid';
import CurrentColorPicker from './currentColorPicker';
import GradientPicker from './gradientPicker';
import Header from './header';
Expand Down Expand Up @@ -102,6 +103,9 @@ function ColorPicker({
useEffect(() => {
if (color) {
load(color);
} else {
// If no color given, load solid black
load(createSolid(0, 0, 0));
}
}, [color, load]);

Expand Down
2 changes: 1 addition & 1 deletion assets/src/edit-story/components/form/color/color.js
Expand Up @@ -64,7 +64,7 @@ function ColorInput({
}

ColorInput.propTypes = {
value: PatternPropType,
value: PropTypes.oneOfType([PatternPropType, PropTypes.string]),
hasGradient: PropTypes.bool,
hasOpacity: PropTypes.bool,
onChange: PropTypes.func.isRequired,
Expand Down
Expand Up @@ -172,7 +172,7 @@ function ColorPreview({
<VisualPreview role="status" style={previewStyle} {...buttonProps} />
<TextualInput
aria-label={`${inputLabel}: ${label}`}
value={hexInputValue}
value={hexInputValue ?? ''}
onChange={handleInputChange}
onBlur={handleInputBlur}
/>
Expand Down
6 changes: 4 additions & 2 deletions assets/src/edit-story/components/form/color/opacityPreview.js
Expand Up @@ -31,6 +31,7 @@ import { _x, __ } from '@wordpress/i18n';
*/
import { PatternPropType } from '../../../types';
import useFocusAndSelect from '../../../utils/useFocusAndSelect';
import { MULTIPLE_VALUE } from '../';
import getPreviewText from './getPreviewText';
import getPreviewOpacity from './getPreviewOpacity';
import ColorBox from './colorBox';
Expand All @@ -47,7 +48,8 @@ const Input = styled(ColorBox).attrs({
`;

function OpacityPreview({ value, onChange }) {
const hasPreviewText = Boolean(getPreviewText(value));
const hasPreviewText =
value !== MULTIPLE_VALUE && Boolean(getPreviewText(value));
const postfix = _x('%', 'Percentage', 'web-stories');
const [inputValue, setInputValue] = useState('');
const ref = useRef();
Expand Down Expand Up @@ -91,7 +93,7 @@ function OpacityPreview({ value, onChange }) {
}

OpacityPreview.propTypes = {
value: PatternPropType,
value: PropTypes.oneOfType([PatternPropType, PropTypes.string]),
onChange: PropTypes.func.isRequired,
};

Expand Down
13 changes: 8 additions & 5 deletions assets/src/edit-story/components/library/panes/text/textPane.js
Expand Up @@ -42,9 +42,11 @@ const PRESETS = [
{
id: 'heading',
title: __('Heading', 'web-stories'),
content: __('Heading', 'web-stories'),
content: `<span style="font-weight: 700">${__(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do a utility that does a very quick and limited (but side-effect-free) construction of content for our constants? Then if/when we switch the format slightly - that'd be easier to keep up-to-date.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean? If we move __() to another function, it'll not be completely side-effect free, as we have tooling depending on this syntax.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Never mind. I'm just slightly concerns about the text constant here (or translation) yielding no-valid HTML (e.g. someone would insert a "&" or "<"). Maybe we should juts wrap __() inside the escapeHTML()?

'Heading',
'web-stories'
)}</span>`,
fontSize: dataFontEm(2),
fontWeight: 700,
font: {
family: 'Open Sans',
service: 'fonts.google.com',
Expand All @@ -53,9 +55,11 @@ const PRESETS = [
{
id: 'subheading',
title: __('Subheading', 'web-stories'),
content: __('Subheading', 'web-stories'),
content: `<span style="font-weight: 600">${__(
'Subheading',
'web-stories'
)}</span>`,
fontSize: dataFontEm(1.5),
fontWeight: 600,
font: {
family: 'Open Sans',
service: 'fonts.google.com',
Expand All @@ -69,7 +73,6 @@ const PRESETS = [
'web-stories'
),
fontSize: dataFontEm(1.1),
fontWeight: 400,
font: {
family: 'Roboto',
service: 'fonts.google.com',
Expand Down
38 changes: 31 additions & 7 deletions assets/src/edit-story/components/panels/stylePreset/panel.js
Expand Up @@ -17,14 +17,16 @@
/**
* External dependencies
*/
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useRef, useEffect, useState } from 'react';

/**
* Internal dependencies
*/
import { useStory } from '../../../app/story';
import stripHTML from '../../../utils/stripHTML';
import { Panel } from './../panel';
import objectWithout from '../../../utils/objectWithout';
import { Panel } from '../panel';
import useRichTextFormatting from '../textStyle/useRichTextFormatting';
import { getShapePresets, getTextPresets } from './utils';
import PresetsHeader from './header';
import Presets from './presets';
Expand Down Expand Up @@ -119,24 +121,46 @@ function StylePresetPanel() {
]
);

const extraPropsToAdd = useRef(null);
dvoytenko marked this conversation as resolved.
Show resolved Hide resolved
const miniPushUpdate = useCallback(
(updater) => {
updateElementsById({
elementIds: selectedElementIds,
properties: (oldProps) => ({
...updater(oldProps),
...extraPropsToAdd.current,
}),
});
extraPropsToAdd.current = null;
},
[selectedElementIds, updateElementsById]
);

const {
handlers: { handleSetColor },
} = useRichTextFormatting(selectedElements, miniPushUpdate);

const handleApplyPreset = useCallback(
(preset) => {
if (isText) {
// @todo Determine this in a better way.
// Only style presets have background text mode set.
const isStylePreset = preset.backgroundTextMode !== undefined;
updateElementsById({
elementIds: selectedElementIds,
properties: isStylePreset ? { ...preset } : { color: preset },
});
if (isStylePreset) {
extraPropsToAdd.current = objectWithout(preset, ['color']);
handleSetColor(preset.color);
} else {
extraPropsToAdd.current = null;
handleSetColor(preset);
}
} else {
updateElementsById({
elementIds: selectedElementIds,
properties: { backgroundColor: preset },
});
}
},
[isText, selectedElementIds, updateElementsById]
[isText, handleSetColor, selectedElementIds, updateElementsById]
);

const colorPresets = isText ? textColors : fillColors;
Expand Down
24 changes: 16 additions & 8 deletions assets/src/edit-story/components/panels/stylePreset/test/panel.js
Expand Up @@ -28,6 +28,7 @@ import { BACKGROUND_TEXT_MODE } from '../../../../constants';
import { getShapePresets, getTextPresets } from '../utils';
import { renderWithTheme } from '../../../../testUtils';
import { TEXT_ELEMENT_DEFAULT_FONT } from '../../../../app/font/defaultFonts';

jest.mock('../utils');

function setupPanel(extraStylePresets, extraStateProps) {
Expand Down Expand Up @@ -91,7 +92,6 @@ describe('Panels/StylePreset', () => {
},
};
const STYLE_PRESET = {
color: TEST_COLOR_2,
backgroundTextMode: BACKGROUND_TEXT_MODE.FILL,
backgroundColor: TEST_COLOR,
};
Expand Down Expand Up @@ -149,13 +149,13 @@ describe('Panels/StylePreset', () => {
expect(newEditButton).toBeDefined();
});

it('should add a text color preset', () => {
it('should add a text color preset if other text styles are default or missing', () => {
const extraStateProps = {
selectedElements: [
{
id: '1',
type: 'text',
color: [TEST_COLOR_2],
content: '<span style="color: rgba(2, 2, 2)">Content</span>',
barklund marked this conversation as resolved.
Show resolved Hide resolved
backgroundTextMode: BACKGROUND_TEXT_MODE.NONE,
font: TEXT_ELEMENT_DEFAULT_FONT,
},
Expand Down Expand Up @@ -193,6 +193,7 @@ describe('Panels/StylePreset', () => {
{
id: '1',
type: 'text',
content: '<span style="color: rgba(2, 2, 2)">Content</span>',
...STYLE_PRESET,
},
],
Expand All @@ -204,7 +205,7 @@ describe('Panels/StylePreset', () => {

getTextPresets.mockImplementation(() => {
return {
textStyles: [STYLE_PRESET],
textStyles: [{ color: TEST_COLOR_2, ...STYLE_PRESET }],
miina marked this conversation as resolved.
Show resolved Hide resolved
};
});

Expand All @@ -217,7 +218,7 @@ describe('Panels/StylePreset', () => {
stylePresets: {
textColors: [],
fillColors: [],
textStyles: [STYLE_PRESET],
textStyles: [{ color: TEST_COLOR_2, ...STYLE_PRESET }],
miina marked this conversation as resolved.
Show resolved Hide resolved
},
},
});
Expand Down Expand Up @@ -365,10 +366,17 @@ describe('Panels/StylePreset', () => {
expect(updateElementsById).toHaveBeenCalledTimes(1);
expect(updateElementsById).toHaveBeenCalledWith({
elementIds: ['1'],
properties: {
color: TEST_COLOR,
},
properties: expect.any(Function),
});
const updaterFunction = updateElementsById.mock.calls[0][0].properties;
const partiallyBlueContent = {
content: 'Hello <span style="color: blue">World</span>',
};
const updatedContent = updaterFunction(partiallyBlueContent);
const expectedContent = {
content: '<span style="color: #010101">Hello World</span>',
};
expect(updatedContent).toStrictEqual(expectedContent);
});
});
});
69 changes: 65 additions & 4 deletions assets/src/edit-story/components/panels/stylePreset/test/utils.js
Expand Up @@ -24,6 +24,7 @@ import {
getTextPresets,
} from '../utils';
import { BACKGROUND_TEXT_MODE } from '../../../../constants';
import objectWithout from '../../../../utils/objectWithout';
import { TEXT_ELEMENT_DEFAULT_FONT } from '../../../../app/font/defaultFonts';

describe('Panels/StylePreset/utils', () => {
Expand Down Expand Up @@ -164,12 +165,13 @@ describe('Panels/StylePreset/utils', () => {
vertical: 0,
horizontal: 0,
},
color: TEST_COLOR,
content: '<span style="color: rgb(1,1,1)">Content</span>',
},
{
type: 'text',
x: 30,
...stylePreset,
content: '<span style="color: rgb(2,2,2)">Content</span>',
...objectWithout(stylePreset, ['color']),
},
];
const stylePresets = {
Expand All @@ -185,6 +187,64 @@ describe('Panels/StylePreset/utils', () => {
expect(presets).toStrictEqual(expected);
});

it('should ignore text color presets for multi-color text fields', () => {
const elements = [
{
type: 'text',
backgroundTextMode: BACKGROUND_TEXT_MODE.NONE,
font: TEXT_ELEMENT_DEFAULT_FONT,
content:
'<span style="color: rgb(1,1,1)">O</span><span style="color: rgb(2,1,1)">K</span>',
},
];
const stylePresets = {
textStyles: [],
textColors: [],
fillColors: [],
};
const expected = {
textColors: [],
textStyles: [],
};
const presets = getTextPresets(elements, stylePresets);
expect(presets).toStrictEqual(expected);
});

it('should use black color when adding text style preset for multi-color text fields', () => {
const stylePreset = {
...STYLE_PRESET,
font: {
family: 'Foo',
fallbacks: ['Bar'],
},
};
const elements = [
{
type: 'text',
x: 30,
content:
'<span style="color: rgb(1,1,1)">O</span><span style="color: rgb(2,1,1)">K</span>',
...objectWithout(stylePreset, ['color']),
},
];
const stylePresets = {
textStyles: [],
textColors: [],
fillColors: [],
};
const expected = {
textColors: [],
textStyles: [
{
...stylePreset,
color: { color: { r: 0, g: 0, b: 0 } },
},
],
};
const presets = getTextPresets(elements, stylePresets);
expect(presets).toStrictEqual(expected);
});

it('should not consider existing presets as new', () => {
const stylePreset = {
...STYLE_PRESET,
Expand All @@ -199,7 +259,7 @@ describe('Panels/StylePreset/utils', () => {
backgroundTextMode: BACKGROUND_TEXT_MODE.NONE,
font: TEXT_ELEMENT_DEFAULT_FONT,
foo: 'bar',
color: TEST_COLOR,
content: '<span style="color: rgb(1,1,1)">Content</span>',
padding: {
vertical: 0,
horizontal: 0,
Expand All @@ -208,7 +268,8 @@ describe('Panels/StylePreset/utils', () => {
{
type: 'text',
x: 30,
...stylePreset,
content: '<span style="color: rgb(2,2,2)">Content</span>',
...objectWithout(stylePreset, ['color']),
},
];
const stylePresets = {
Expand Down