Skip to content

Commit c5310f8

Browse files
Added portalHost prop to DatePicker component.
When passed a shadow root through the portalHost prop, all portals will be created as children of that shadow root. This enables style encapsulation for consumers that are using the shadow DOM.
1 parent a641b91 commit c5310f8

File tree

7 files changed

+44
-11
lines changed

7 files changed

+44
-11
lines changed

docs/datepicker.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ General datepicker component.
9393
| `value` | `string` | | |
9494
| `weekLabel` | `string` | | |
9595
| `withPortal` | `bool` | `false` | |
96+
| `portalId` | `string` | | |
97+
| `portalHost` | `instanceOf(ShadowRoot)` | | When set, portals will be attached to this ShadowRoot instead of the document body. |
9698
| `wrapperClassName` | `string` |
9799
| |
98100
| `yearItemNumber` | `number` | `12` | |

docs/popper_component.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@
1212
| `popperPlacement` | `enumpopperPlacementPositions` | `"bottom-start"` | |
1313
| `popperProps` | `object` | `{}` | |
1414
| `portalId` | `string` | | |
15+
| `portalHost` | `instanceOf(ShadowRoot)` | | |
1516
| `targetComponent` | `element` | | |
1617
| `wrapperClassName` | `string` | | |

docs/portal.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
`portal` (component)
2-
====================
1+
# `portal` (component)
32

4-
5-
| name | type | default value | description |
6-
|---|---|---|---|
7-
|`children`|`any`|||
8-
|`portalId`|`string`|||
3+
| name | type | default value | description |
4+
| ------------ | ------------------------ | ------------- | ----------- |
5+
| `children` | `any` | | |
6+
| `portalId` | `string` | | |
7+
| `portalHost` | `instanceOf(ShadowRoot)` | | |

src/index.jsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ export default class DatePicker extends React.Component {
227227
weekLabel: PropTypes.string,
228228
withPortal: PropTypes.bool,
229229
portalId: PropTypes.string,
230+
portalHost: PropTypes.instanceOf(ShadowRoot),
230231
yearItemNumber: PropTypes.number,
231232
yearDropdownItemNumber: PropTypes.number,
232233
shouldCloseOnSelect: PropTypes.bool,
@@ -1048,7 +1049,12 @@ export default class DatePicker extends React.Component {
10481049

10491050
if (this.state.open && this.props.portalId) {
10501051
portalContainer = (
1051-
<Portal portalId={this.props.portalId}>{portalContainer}</Portal>
1052+
<Portal
1053+
portalId={this.props.portalId}
1054+
portalHost={this.props.portalHost}
1055+
>
1056+
{portalContainer}
1057+
</Portal>
10521058
);
10531059
}
10541060

@@ -1066,6 +1072,7 @@ export default class DatePicker extends React.Component {
10661072
wrapperClassName={this.props.wrapperClassName}
10671073
hidePopper={!this.isCalendarOpen()}
10681074
portalId={this.props.portalId}
1075+
portalHost={this.props.portalHost}
10691076
popperModifiers={this.props.popperModifiers}
10701077
targetComponent={this.renderInputContainer()}
10711078
popperContainer={this.props.popperContainer}

src/popper_component.jsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export default class PopperComponent extends React.Component {
3131
enableTabLoop: PropTypes.bool,
3232
popperOnKeyDown: PropTypes.func,
3333
portalId: PropTypes.string,
34+
portalHost: PropTypes.instanceOf(ShadowRoot),
3435
};
3536

3637
render() {
@@ -46,6 +47,7 @@ export default class PopperComponent extends React.Component {
4647
enableTabLoop,
4748
popperOnKeyDown,
4849
portalId,
50+
portalHost,
4951
} = this.props;
5052

5153
let popper;
@@ -79,7 +81,11 @@ export default class PopperComponent extends React.Component {
7981
}
8082

8183
if (portalId && !hidePopper) {
82-
popper = <Portal portalId={portalId}>{popper}</Portal>;
84+
popper = (
85+
<Portal portalId={portalId} portalHost={portalHost}>
86+
{popper}
87+
</Portal>
88+
);
8389
}
8490

8591
const wrapperClasses = classnames(

src/portal.jsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export default class Portal extends React.Component {
66
static propTypes = {
77
children: PropTypes.any,
88
portalId: PropTypes.string,
9+
portalHost: PropTypes.instanceOf(ShadowRoot),
910
};
1011

1112
constructor(props) {
@@ -14,11 +15,13 @@ export default class Portal extends React.Component {
1415
}
1516

1617
componentDidMount() {
17-
this.portalRoot = document.getElementById(this.props.portalId);
18+
this.portalRoot = (this.props.portalHost || document).getElementById(
19+
this.props.portalId
20+
);
1821
if (!this.portalRoot) {
1922
this.portalRoot = document.createElement("div");
2023
this.portalRoot.setAttribute("id", this.props.portalId);
21-
document.body.appendChild(this.portalRoot);
24+
(this.props.portalHost || document.body).appendChild(this.portalRoot);
2225
}
2326
this.portalRoot.appendChild(this.el);
2427
}

test/datepicker_test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,21 @@ describe("DatePicker", () => {
112112
expect(datePicker.calendar).to.exist;
113113
});
114114

115+
it("should render the calendar in the portalHost prop when provided", () => {
116+
var root = document.createElement("div");
117+
var shadow = root.attachShadow({ mode: "closed" });
118+
var appHost = document.createElement("div");
119+
shadow.appendChild(appHost);
120+
var datePicker = ReactDOM.render(
121+
<DatePicker portalId="test-portal" portalHost={shadow} />,
122+
appHost
123+
);
124+
var dateInput = datePicker.input;
125+
TestUtils.Simulate.click(ReactDOM.findDOMNode(dateInput));
126+
expect(datePicker.calendar).to.exist;
127+
expect(shadow.getElementById("test-portal")).to.exist;
128+
});
129+
115130
it("should not set open state when it is disabled and gets clicked", function () {
116131
var datePicker = TestUtils.renderIntoDocument(<DatePicker disabled />);
117132
var dateInput = datePicker.input;

0 commit comments

Comments
 (0)