Skip to content

Commit 77eed70

Browse files
authored
Merge pull request #13 from vegerot/base-target
fix: teach <Helmet> to accept base tag with only 'target' attribute
2 parents fd1df7a + ae2267c commit 77eed70

File tree

4 files changed

+94
-4
lines changed

4 files changed

+94
-4
lines changed

__tests__/api/base.test.tsx

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,21 @@ describe('base tag', () => {
3030
expect(existingTags).toHaveLength(0);
3131
});
3232

33-
it('tags without \'href\' are not accepted', () => {
34-
renderClient(<Helmet base={{ property: 'won\'t work' }} />);
33+
it("tags with only 'target' are accepted", () => {
34+
renderClient(<Helmet base={{ target: '_blank' }} />);
35+
const existingTags = [...document.head.querySelectorAll(`base[${HELMET_ATTRIBUTE}]`)];
36+
37+
expect(existingTags).toBeDefined();
38+
expect(existingTags).toHaveLength(1);
39+
const firstTag = existingTags[0]!;
40+
expect(firstTag).toBeInstanceOf(Element);
41+
expect(firstTag.getAttribute).toBeDefined();
42+
expect(firstTag).toHaveAttribute('target', '_blank');
43+
expect(firstTag).not.toHaveAttribute('href');
44+
});
45+
46+
it("tags without 'href' or 'target' are not accepted", () => {
47+
renderClient(<Helmet base={{ property: "won't work" }} />);
3548
const existingTags = document.head.querySelectorAll(`base[${HELMET_ATTRIBUTE}]`);
3649

3750
expect(existingTags).toBeDefined();
@@ -97,7 +110,25 @@ describe('base tag', () => {
97110
expect(existingTags).toHaveLength(0);
98111
});
99112

100-
it('tags without \'href\' are not accepted', () => {
113+
it("tags with only 'target' are accepted", () => {
114+
renderClient(
115+
<Helmet>
116+
<base target="_blank" />
117+
</Helmet>,
118+
);
119+
120+
const existingTags = [...document.head.querySelectorAll(`base[${HELMET_ATTRIBUTE}]`)];
121+
122+
expect(existingTags).toBeDefined();
123+
expect(existingTags).toHaveLength(1);
124+
const firstTag = existingTags[0]!;
125+
expect(firstTag).toBeInstanceOf(Element);
126+
expect(firstTag.getAttribute).toBeDefined();
127+
expect(firstTag).toHaveAttribute('target', '_blank');
128+
expect(firstTag).not.toHaveAttribute('href');
129+
});
130+
131+
it("tags without 'href' or 'target' are not accepted", () => {
101132
/* eslint-disable react/no-unknown-property */
102133
renderClient(
103134
<Helmet>

__tests__/server/base.test.tsx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,30 @@ describe('server', () => {
3535
expect(head?.base.toString).toBeDefined();
3636
expect(head?.base.toString()).toMatchSnapshot();
3737
});
38+
39+
it("renders base tag with only 'target' as React component", () => {
40+
const head = renderContextServer(<Helmet base={{ target: '_blank' }} />);
41+
42+
expect(head?.base).toBeDefined();
43+
expect(head?.base.toComponent).toBeDefined();
44+
45+
const baseComponent = head?.base.toComponent();
46+
47+
expect(baseComponent).toStrictEqual(isArray);
48+
expect(baseComponent).toHaveLength(1);
49+
50+
const markup = renderToStaticMarkup(baseComponent);
51+
expect(markup).toContain('target="_blank"');
52+
expect(markup).not.toContain('href=');
53+
});
54+
55+
it("renders base tag with only 'target' as string", () => {
56+
const head = renderContextServer(<Helmet base={{ target: '_blank' }} />);
57+
expect(head?.base).toBeDefined();
58+
expect(head?.base.toString).toBeDefined();
59+
expect(head?.base.toString()).toContain('target="_blank"');
60+
expect(head?.base.toString()).not.toContain('href=');
61+
});
3862
});
3963

4064
describe('Declarative API', () => {
@@ -73,5 +97,39 @@ describe('server', () => {
7397
expect(head!.base.toString).toBeDefined();
7498
expect(head?.base.toString()).toMatchSnapshot();
7599
});
100+
101+
it("renders base tag with only 'target' as React component", () => {
102+
const head = renderContextServer(
103+
<Helmet>
104+
<base target="_blank" />
105+
</Helmet>,
106+
);
107+
108+
expect(head?.base).toBeDefined();
109+
expect(head?.base.toComponent).toBeDefined();
110+
111+
const baseComponent = head?.base.toComponent();
112+
113+
expect(baseComponent).toStrictEqual(isArray);
114+
expect(baseComponent).toHaveLength(1);
115+
116+
const markup = renderToStaticMarkup(baseComponent);
117+
118+
expect(markup).toContain('target="_blank"');
119+
expect(markup).not.toContain('href=');
120+
});
121+
122+
it("renders base tag with only 'target' as string", () => {
123+
const head = renderContextServer(
124+
<Helmet>
125+
<base target="_blank" />
126+
</Helmet>,
127+
);
128+
129+
expect(head?.base).toBeDefined();
130+
expect(head?.base.toString).toBeDefined();
131+
expect(head?.base.toString()).toContain('target="_blank"');
132+
expect(head?.base.toString()).not.toContain('href=');
133+
});
76134
});
77135
});

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export enum TAG_PROPERTIES {
99
PROPERTY = 'property',
1010
REL = 'rel',
1111
SRC = 'src',
12+
TARGET = 'target',
1213
}
1314

1415
export enum ATTRIBUTE_NAMES {

src/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export function aggregateBaseProps(
103103
): BaseProps | undefined {
104104
for (let i = props.length - 1; i >= 0; --i) {
105105
const res = props[i]![1].base;
106-
if (res?.href) return res;
106+
if (res?.href || res?.target) return res;
107107
}
108108
return undefined;
109109
}

0 commit comments

Comments
 (0)