Skip to content

Commit 0688571

Browse files
author
Yannick Croissant
authoredApr 1, 2021
feat(ts): convert Template component to TypeScript (#4703)
1 parent 8e3c406 commit 0688571

File tree

5 files changed

+57
-60
lines changed

5 files changed

+57
-60
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,42 @@
11
/** @jsx h */
22

33
import { h, Component } from 'preact';
4-
import PropTypes from 'prop-types';
54
import { renderTemplate, isEqual } from '../../lib/utils';
5+
import { PreparedTemplateProps } from '../../lib/utils/prepareTemplateProps';
6+
import { Templates } from '../../types';
67

7-
class Template extends Component {
8-
shouldComponentUpdate(nextProps) {
8+
const defaultProps = {
9+
data: {},
10+
rootTagName: 'div',
11+
useCustomCompileOptions: {},
12+
templates: {},
13+
templatesConfig: {},
14+
};
15+
16+
type TemplateProps = {
17+
data?: Record<string, any>;
18+
rootProps?: Record<string, any>;
19+
rootTagName?: keyof h.JSX.IntrinsicElements;
20+
templateKey: string;
21+
bindEvent?: (...args: any[]) => string;
22+
} & PreparedTemplateProps<Templates> &
23+
Readonly<typeof defaultProps>;
24+
25+
// @TODO: Template should be a generic and receive TData to pass to Templates (to avoid TTemplateData to be set as `any`)
26+
class Template extends Component<TemplateProps> {
27+
public static readonly defaultProps = defaultProps;
28+
29+
public shouldComponentUpdate(nextProps: TemplateProps) {
930
return (
1031
!isEqual(this.props.data, nextProps.data) ||
1132
this.props.templateKey !== nextProps.templateKey ||
1233
!isEqual(this.props.rootProps, nextProps.rootProps)
1334
);
1435
}
1536

16-
render() {
37+
public render() {
1738
const RootTagName = this.props.rootTagName;
39+
1840
const useCustomCompileOptions = this.props.useCustomCompileOptions[
1941
this.props.templateKey
2042
];
@@ -46,39 +68,4 @@ class Template extends Component {
4668
}
4769
}
4870

49-
Template.propTypes = {
50-
data: PropTypes.object,
51-
rootProps: PropTypes.object,
52-
rootTagName: PropTypes.string,
53-
templateKey: PropTypes.string,
54-
templates: PropTypes.objectOf(
55-
PropTypes.oneOfType([PropTypes.string, PropTypes.func])
56-
),
57-
templatesConfig: PropTypes.shape({
58-
helpers: PropTypes.objectOf(PropTypes.func),
59-
// https://github.com/twitter/hogan.js/#compilation-options
60-
compileOptions: PropTypes.shape({
61-
asString: PropTypes.bool,
62-
sectionTags: PropTypes.arrayOf(
63-
PropTypes.shape({
64-
o: PropTypes.string,
65-
c: PropTypes.string,
66-
})
67-
),
68-
delimiters: PropTypes.string,
69-
disableLambda: PropTypes.bool,
70-
}),
71-
}),
72-
useCustomCompileOptions: PropTypes.objectOf(PropTypes.bool),
73-
bindEvent: PropTypes.func,
74-
};
75-
76-
Template.defaultProps = {
77-
data: {},
78-
rootTagName: 'div',
79-
useCustomCompileOptions: {},
80-
templates: {},
81-
templatesConfig: {},
82-
};
83-
8471
export default Template;

‎src/components/Template/__tests__/Template-test.js ‎src/components/Template/__tests__/Template-test.tsx

+11-16
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
import { h } from 'preact';
44
import Template from '../Template';
55
import { mount, shallow } from 'enzyme';
6+
import { ReactElementLike } from 'prop-types';
67

78
function getProps({
89
templates = { test: '' },
910
data = {},
1011
templateKey = 'test',
1112
rootProps = {},
1213
useCustomCompileOptions = {},
13-
templatesConfig = { helper: {}, compileOptions: {} },
14+
templatesConfig = { helpers: {}, compileOptions: {} },
1415
...props
1516
}) {
1617
return {
@@ -30,16 +31,16 @@ describe('Template', () => {
3031
templates: { test: 'it configures compilation <%options%>' },
3132
data: { options: 'delimiters' },
3233
useCustomCompileOptions: { test: true },
33-
templatesConfig: { compileOptions: { delimiters: '<% %>' } },
34+
templatesConfig: { helpers: {}, compileOptions: { delimiters: '<% %>' } },
3435
});
35-
const wrapper = mount(<Template {...props} />);
36+
const wrapper = mount((<Template {...props} />) as ReactElementLike);
3637

3738
expect(wrapper).toMatchSnapshot();
3839
});
3940

4041
it('can configure custom rootTagName', () => {
4142
const props = getProps({ rootTagName: 'span' });
42-
const wrapper = mount(<Template {...props} />);
43+
const wrapper = mount((<Template {...props} />) as ReactElementLike);
4344

4445
expect(wrapper).toMatchSnapshot();
4546
});
@@ -50,25 +51,19 @@ describe('Template', () => {
5051
const props = getProps({
5152
rootProps: { className: 'className', onClick },
5253
});
53-
const wrapper = mount(<Template {...props} />);
54+
const wrapper = mount((<Template {...props} />) as ReactElementLike);
5455

5556
expect(wrapper).toMatchSnapshot();
5657
});
5758

5859
describe('shouldComponentUpdate', () => {
59-
let container;
60-
61-
beforeEach(() => {
62-
container = document.createElement('div');
63-
});
64-
6560
it('does not call render when no change in data', () => {
6661
const props = getProps({
6762
data: {
6863
items: [],
6964
},
7065
});
71-
const wrapper = shallow(<Template {...props} />, container);
66+
const wrapper = shallow((<Template {...props} />) as ReactElementLike);
7267
const onRender = jest.spyOn(wrapper.instance(), 'render');
7368

7469
wrapper.setProps({ data: { items: [] } });
@@ -82,7 +77,7 @@ describe('Template', () => {
8277
items: [],
8378
},
8479
});
85-
const wrapper = shallow(<Template {...props} />, container);
80+
const wrapper = shallow((<Template {...props} />) as ReactElementLike);
8681
const onRender = jest.spyOn(wrapper.instance(), 'render');
8782

8883
wrapper.setProps({ data: { items: [1] } });
@@ -92,7 +87,7 @@ describe('Template', () => {
9287

9388
it('calls render when templateKey changes', () => {
9489
const props = getProps({});
95-
const wrapper = shallow(<Template {...props} />, container);
90+
const wrapper = shallow((<Template {...props} />) as ReactElementLike);
9691
const onRender = jest.spyOn(wrapper.instance(), 'render');
9792

9893
wrapper.setProps({
@@ -107,7 +102,7 @@ describe('Template', () => {
107102

108103
it('calls render when rootProps changes', () => {
109104
const props = getProps({});
110-
const wrapper = shallow(<Template {...props} />, container);
105+
const wrapper = shallow((<Template {...props} />) as ReactElementLike);
111106
const onRender = jest.spyOn(wrapper.instance(), 'render');
112107

113108
wrapper.setProps({
@@ -125,7 +120,7 @@ describe('Template', () => {
125120
className: 'initialClassName',
126121
},
127122
});
128-
const wrapper = shallow(<Template {...props} />, container);
123+
const wrapper = shallow((<Template {...props} />) as ReactElementLike);
129124
const onRender = jest.spyOn(wrapper.instance(), 'render');
130125

131126
wrapper.setProps({

‎src/lib/utils/prepareTemplateProps.ts

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
import uniq from './uniq';
2-
import { Template } from '../../types';
2+
import { Templates } from '../../types';
33

4-
type TemplatesConfig = Record<string, unknown>;
5-
6-
type Templates = {
7-
[key: string]: Template<any>;
4+
type TemplatesConfig = {
5+
helpers?: Record<
6+
string,
7+
(text: string, render: (value: any) => string) => string
8+
>;
9+
// https://github.com/twitter/hogan.js/#compilation-options
10+
compileOptions?: {
11+
asString?: boolean;
12+
sectionTags?: Array<{
13+
o?: string;
14+
c?: string;
15+
}>;
16+
delimiters?: string;
17+
disableLambda?: boolean;
18+
};
819
};
920

1021
export type PreparedTemplateProps<TTemplates extends Templates> = {

‎src/types/widget.ts

+4
Original file line numberDiff line numberDiff line change
@@ -548,3 +548,7 @@ export type UnknownWidgetFactory = WidgetFactory<any, any, any>;
548548
export type TemplateWithBindEvent<TTemplateData = void> =
549549
| string
550550
| ((data: TTemplateData, bindEvent: BindEventForHits) => string);
551+
552+
export type Templates = {
553+
[key: string]: Template<any> | TemplateWithBindEvent<any> | undefined;
554+
};

0 commit comments

Comments
 (0)
Please sign in to comment.