Skip to content

Commit

Permalink
Inline text formatting (#1323)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Morten Barklund committed May 6, 2020
1 parent 8c7421a commit dd191a4
Show file tree
Hide file tree
Showing 68 changed files with 4,352 additions and 682 deletions.
14 changes: 7 additions & 7 deletions .github/workflows/continuous-integration.yml
Expand Up @@ -71,7 +71,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 All @@ -96,8 +96,8 @@ jobs:
- name: Annotate Code Linting Results
uses: ataylorme/eslint-annotate-action@1.0.4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
report-json: "build/lint-js-report.json"
repo-token: '${{ secrets.GITHUB_TOKEN }}'
report-json: 'build/lint-js-report.json'

markdownlint:
name: Markdown Code Style
Expand All @@ -113,7 +113,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 Expand Up @@ -148,7 +148,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 Expand Up @@ -328,7 +328,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 Expand Up @@ -405,7 +405,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
2 changes: 1 addition & 1 deletion .github/workflows/release-assets.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">${__(
'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,18 +17,20 @@
/**
* 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 objectWithout from '../../../utils/objectWithout';
import { Panel } from '../panel';
import useRichTextFormatting from '../textStyle/useRichTextFormatting';
import {
COLOR_PRESETS_PER_ROW,
STYLE_PRESETS_PER_ROW,
} from '../../../constants';
import { Panel } from './../panel';
import { getShapePresets, getTextPresets } from './utils';
import PresetsHeader from './header';
import Presets from './presets';
Expand Down Expand Up @@ -123,24 +125,46 @@ function StylePresetPanel() {
]
);

const extraPropsToAdd = useRef(null);
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>',
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 }],
};
});

Expand All @@ -217,7 +218,7 @@ describe('Panels/StylePreset', () => {
stylePresets: {
textColors: [],
fillColors: [],
textStyles: [STYLE_PRESET],
textStyles: [{ color: TEST_COLOR_2, ...STYLE_PRESET }],
},
},
});
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);
});
});
});

0 comments on commit dd191a4

Please sign in to comment.