Skip to content

Commit 0bae8fc

Browse files
committed
Introduce extensible/provideExtensions API
Components no longer decide whether they are decorated or replaced — they just declare themselves as extensible via `extensible('Name', Component)`. The extension provider (e.g. inline editing) decides the extension type by placing components into either `decorators` or `alternatives` when calling `provideExtensions`. REDMINE-21261
1 parent 0675366 commit 0bae8fc

26 files changed

+338
-222
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import React from 'react';
2+
3+
import '@testing-library/jest-dom/extend-expect'
4+
import {render} from '@testing-library/react'
5+
import {
6+
extensible,
7+
provideExtensions,
8+
clearExtensions
9+
} from 'frontend/extensions';
10+
import {StaticPreview} from 'frontend/useScrollPositionLifecycle';
11+
12+
describe('extensions', () => {
13+
afterEach(() => {
14+
clearExtensions();
15+
});
16+
17+
describe('extensible with decorator', () => {
18+
it('wraps component with decorator receiving same props', () => {
19+
const TestComponent = extensible('TestComponent', function TestComponent({text}) {
20+
return <span>{text} Component</span>;
21+
});
22+
23+
provideExtensions({
24+
decorators: {
25+
TestComponent({text, children}) {
26+
return <div>{text} Decorator{children}</div>;
27+
}
28+
}
29+
});
30+
31+
const {container} = render(<TestComponent text="Hello" />);
32+
33+
expect(container).toHaveTextContent('Hello Decorator');
34+
expect(container).toHaveTextContent('Hello Component');
35+
});
36+
37+
it('renders original component when no extensions provided', () => {
38+
const TestComponent = extensible('TestComponent', function TestComponent() {
39+
return <span>Component</span>;
40+
});
41+
42+
const {container} = render(<TestComponent />);
43+
44+
expect(container).toHaveTextContent('Component');
45+
});
46+
47+
it('renders original component in static preview', () => {
48+
const TestComponent = extensible('TestComponent', function TestComponent() {
49+
return <span>Component</span>;
50+
});
51+
52+
provideExtensions({
53+
decorators: {
54+
TestComponent({children}) {
55+
return <div>Decorator{children}</div>;
56+
}
57+
}
58+
});
59+
60+
const {container} = render(
61+
<StaticPreview>
62+
<TestComponent />
63+
</StaticPreview>
64+
);
65+
66+
expect(container).toHaveTextContent('Component');
67+
expect(container).not.toHaveTextContent('Decorator');
68+
});
69+
});
70+
71+
describe('extensible with alternative', () => {
72+
it('renders alternative instead of original', () => {
73+
const TestComponent = extensible('TestComponent', function TestComponent() {
74+
return <span>Original</span>;
75+
});
76+
77+
provideExtensions({
78+
alternatives: {
79+
TestComponent() {
80+
return <span>Alternative</span>;
81+
}
82+
}
83+
});
84+
85+
const {container} = render(<TestComponent />);
86+
87+
expect(container).toHaveTextContent('Alternative');
88+
expect(container).not.toHaveTextContent('Original');
89+
});
90+
91+
it('renders original component in static preview', () => {
92+
const TestComponent = extensible('TestComponent', function TestComponent() {
93+
return <span>Original</span>;
94+
});
95+
96+
provideExtensions({
97+
alternatives: {
98+
TestComponent() {
99+
return <span>Alternative</span>;
100+
}
101+
}
102+
});
103+
104+
const {container} = render(
105+
<StaticPreview>
106+
<TestComponent />
107+
</StaticPreview>
108+
);
109+
110+
expect(container).toHaveTextContent('Original');
111+
expect(container).not.toHaveTextContent('Alternative');
112+
});
113+
});
114+
115+
describe('provideExtensions', () => {
116+
it('replaces previous extensions', () => {
117+
const TestComponent = extensible('TestComponent', function TestComponent() {
118+
return <span>Original</span>;
119+
});
120+
121+
provideExtensions({
122+
alternatives: {
123+
TestComponent() {
124+
return <span>First</span>;
125+
}
126+
}
127+
});
128+
129+
provideExtensions({
130+
alternatives: {
131+
TestComponent() {
132+
return <span>Second</span>;
133+
}
134+
}
135+
});
136+
137+
const {container} = render(<TestComponent />);
138+
139+
expect(container).toHaveTextContent('Second');
140+
expect(container).not.toHaveTextContent('First');
141+
});
142+
});
143+
});

entry_types/scrolled/package/spec/frontend/inlineEditing-spec.js

Lines changed: 0 additions & 51 deletions
This file was deleted.

entry_types/scrolled/package/spec/frontend/inlineEditingWithLoadedComponents-spec.js

Lines changed: 61 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,86 +2,87 @@ import React from 'react';
22

33
import '@testing-library/jest-dom/extend-expect'
44
import {render} from '@testing-library/react'
5-
import {
6-
withInlineEditingDecorator,
7-
withInlineEditingAlternative,
8-
loadInlineEditingComponents
9-
} from 'frontend/inlineEditing';
5+
import {extensible, clearExtensions} from 'frontend/extensions';
6+
import {loadInlineEditingComponents} from 'frontend/inlineEditing';
107
import {StaticPreview} from 'frontend/useScrollPositionLifecycle';
118

129
jest.mock('frontend/inlineEditing/components', () => ({
13-
TestDecorator({text, children}) {
14-
return <div>{text} Decorator<div>{children}</div></div>;
15-
},
16-
17-
TestAlternative({text, children}) {
18-
return <div>{text} Alternative</div>;
10+
extensions: {
11+
decorators: {
12+
TestComponent({text, children}) {
13+
return <div>{text} Decorator<div>{children}</div></div>;
14+
}
15+
},
16+
alternatives: {
17+
TestAlternative({text}) {
18+
return <div>{text} Alternative</div>;
19+
}
20+
}
1921
}
2022
}));
2123

22-
describe('inlineEditing', () => {
23-
// see inlineEditing-spec for cases where inline editing components
24-
// are not loaded.
25-
26-
describe('when inline editing components are loaded', () => {
27-
describe('withInlineEditingDecorator', () => {
28-
it('wraps component with decorator receiving same props', async () => {
29-
const TestComponent = withInlineEditingDecorator('TestDecorator', function TestComponent({text}) {
30-
return <div>{text} Test</div>;
31-
});
32-
33-
await loadInlineEditingComponents();
34-
const {container} = render(<TestComponent text="Hello" />);
24+
describe('extensions with loaded inline editing components', () => {
25+
afterAll(() => {
26+
clearExtensions();
27+
});
3528

36-
expect(container).toHaveTextContent('Hello Decorator');
37-
expect(container).toHaveTextContent('Hello Test');
29+
describe('decorator', () => {
30+
it('wraps component with decorator receiving same props', async () => {
31+
const TestComponent = extensible('TestComponent', function TestComponent({text}) {
32+
return <div>{text} Test</div>;
3833
});
3934

40-
it('renders component without decorator in static preview', async () => {
41-
const TestComponent = withInlineEditingDecorator('TestDecorator', function TestComponent({text}) {
42-
return <div>{text} Test</div>;
43-
});
35+
await loadInlineEditingComponents();
36+
const {container} = render(<TestComponent text="Hello" />);
4437

45-
await loadInlineEditingComponents();
46-
const {container} = render(
47-
<StaticPreview>
48-
<TestComponent text="Hello" />
49-
</StaticPreview>
50-
);
38+
expect(container).toHaveTextContent('Hello Decorator');
39+
expect(container).toHaveTextContent('Hello Test');
40+
});
5141

52-
expect(container).not.toHaveTextContent('Decorator');
53-
expect(container).toHaveTextContent('Hello Test');
42+
it('renders component without decorator in static preview', async () => {
43+
const TestComponent = extensible('TestComponent', function TestComponent({text}) {
44+
return <div>{text} Test</div>;
5445
});
55-
});
5646

57-
describe('withInlineEditingAlternative', () => {
58-
it('renders alternative component instead', async () => {
59-
const TestComponent = withInlineEditingAlternative('TestAlternative', function TestComponent({text}) {
60-
return <div>{text} Test</div>;
61-
});
47+
await loadInlineEditingComponents();
48+
const {container} = render(
49+
<StaticPreview>
50+
<TestComponent text="Hello" />
51+
</StaticPreview>
52+
);
6253

63-
await loadInlineEditingComponents();
64-
const {container} = render(<TestComponent text="Hello" />);
54+
expect(container).not.toHaveTextContent('Decorator');
55+
expect(container).toHaveTextContent('Hello Test');
56+
});
57+
});
6558

66-
expect(container).toHaveTextContent('Hello Alternative');
67-
expect(container).not.toHaveTextContent('Test');
59+
describe('alternative', () => {
60+
it('renders alternative component instead', async () => {
61+
const TestComponent = extensible('TestAlternative', function TestComponent({text}) {
62+
return <div>{text} Test</div>;
6863
});
6964

70-
it('renders original component in static preview', async () => {
71-
const TestComponent = withInlineEditingAlternative('TestAlternative', function TestComponent({text}) {
72-
return <div>{text} Test</div>;
73-
});
65+
await loadInlineEditingComponents();
66+
const {container} = render(<TestComponent text="Hello" />);
7467

75-
await loadInlineEditingComponents();
76-
const {container} = render(
77-
<StaticPreview>
78-
<TestComponent text="Hello" />
79-
</StaticPreview>
80-
);
68+
expect(container).toHaveTextContent('Hello Alternative');
69+
expect(container).not.toHaveTextContent('Test');
70+
});
8171

82-
expect(container).not.toHaveTextContent('Alternative');
83-
expect(container).toHaveTextContent('Hello Test');
72+
it('renders original component in static preview', async () => {
73+
const TestComponent = extensible('TestAlternative', function TestComponent({text}) {
74+
return <div>{text} Test</div>;
8475
});
76+
77+
await loadInlineEditingComponents();
78+
const {container} = render(
79+
<StaticPreview>
80+
<TestComponent text="Hello" />
81+
</StaticPreview>
82+
);
83+
84+
expect(container).not.toHaveTextContent('Alternative');
85+
expect(container).toHaveTextContent('Hello Test');
8586
});
8687
});
8788
});
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import {withInlineEditingAlternative} from './inlineEditing';
1+
import {extensible} from './extensions';
22

3-
export const ActionButton = withInlineEditingAlternative('ActionButton', function ActionButton() {
3+
export const ActionButton = extensible('ActionButton', function ActionButton() {
44
return null;
55
});
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import {withInlineEditingAlternative} from './inlineEditing';
1+
import {extensible} from './extensions';
22

3-
export const ActionButtons = withInlineEditingAlternative('ActionButtons', function ActionButtons() {
3+
export const ActionButtons = extensible('ActionButtons', function ActionButtons() {
44
return null;
55
});

entry_types/scrolled/package/src/frontend/Backdrop/BackgroundContentElement.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import React, {useMemo} from 'react';
33
import {ContentElement} from '../ContentElement';
44
import {useSectionLifecycle} from '../useSectionLifecycle';
55

6-
import {withInlineEditingDecorator} from '../inlineEditing';
6+
import {extensible} from '../extensions';
77

8-
export const BackgroundContentElement = withInlineEditingDecorator(
9-
'BackgroundContentElementDecorator',
8+
export const BackgroundContentElement = extensible(
9+
'BackgroundContentElement',
1010
function BackgroundContentElement({
1111
contentElement, isIntersecting, onMotifAreaUpdate, containerDimension
1212
}) {

entry_types/scrolled/package/src/frontend/Backdrop/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import classNames from 'classnames';
33

4-
import {withInlineEditingDecorator} from '../inlineEditing';
4+
import {extensible} from '../extensions';
55
import useDimension from '../useDimension';
66
import {useSectionLifecycle} from '../useSectionLifecycle';
77

@@ -10,7 +10,7 @@ import {BackgroundAsset} from './BackgroundAsset';
1010
import styles from '../Backdrop.module.css';
1111
import sharedTransitionStyles from '../transitions/shared.module.css';
1212

13-
export const Backdrop = withInlineEditingDecorator('BackdropDecorator', function Backdrop(props) {
13+
export const Backdrop = extensible('Backdrop', function Backdrop(props) {
1414
const [containerDimension, setContainerRef] = useDimension();
1515
const {shouldLoad} = useSectionLifecycle();
1616

0 commit comments

Comments
 (0)