Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ type DraggableData = {
| `grid` | `[number, number]` | - | Snap to grid `[x, y]` |
| `handle` | `string` | - | CSS selector for the drag handle |
| `nodeRef` | `React.RefObject` | - | Ref to the DOM element. Required for React Strict Mode |
| `nonce` | `string` | - | Nonce for Content Security Policy. Applied to dynamically created `<style>` tags |
| `offsetParent` | `HTMLElement` | - | Custom offsetParent for drag calculations |
| `onDrag` | `DraggableEventHandler` | - | Called while dragging |
| `onMouseDown` | `(e: MouseEvent) => void` | - | Called on mouse down |
Expand Down Expand Up @@ -147,6 +148,7 @@ See [React-Resizable](https://github.com/react-grid-layout/react-resizable) and
- `handle`
- `nodeRef`
- `offsetParent`
- `nonce`
- `onDrag`
- `onMouseDown`
- `onStart`
Expand Down
11 changes: 10 additions & 1 deletion lib/DraggableCore.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export type DraggableCoreDefaultProps = {
onStop: DraggableEventHandler,
onMouseDown: (e: MouseEvent) => void,
scale: number,
nonce: ?string,
};

export type DraggableCoreProps = {
Expand All @@ -60,6 +61,7 @@ export type DraggableCoreProps = {
grid: [number, number],
handle: string,
nodeRef?: ?React.ElementRef<any>,
nonce?: ?string,
};

//
Expand Down Expand Up @@ -213,6 +215,12 @@ export default class DraggableCore extends React.Component<DraggableCoreProps> {
*/
scale: PropTypes.number,

/**
* `nonce`, if set, will be added to the dynamically created <style> tag
* for Content Security Policy compliance
*/
nonce: PropTypes.string,

/**
* These properties should be defined on the child, not here.
*/
Expand All @@ -231,6 +239,7 @@ export default class DraggableCore extends React.Component<DraggableCoreProps> {
onStop: function(){},
onMouseDown: function(){},
scale: 1,
nonce: undefined,
};

dragging: boolean = false;
Expand Down Expand Up @@ -336,7 +345,7 @@ export default class DraggableCore extends React.Component<DraggableCoreProps> {

// Add a style to the body to disable user-select. This prevents text from
// being selected all over the page.
if (this.props.enableUserSelectHack) addUserSelectStyles(ownerDocument);
if (this.props.enableUserSelectHack) addUserSelectStyles(ownerDocument, this.props.nonce);

// Initiate dragging. Set the current x and y as offsets
// so we know how much we've moved during the drag. This allows us
Expand Down
6 changes: 5 additions & 1 deletion lib/utils/domFns.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,17 @@ export function getTouchIdentifier(e: MouseTouchEvent): ?number {
// Useful for preventing blue highlights all over everything when dragging.

// Note we're passing `document` b/c we could be iframed
export function addUserSelectStyles(doc: ?Document) {
export function addUserSelectStyles(doc: ?Document, nonce: ?string) {
if (!doc) return;
let styleEl = doc.getElementById('react-draggable-style-el');
if (!styleEl) {
styleEl = doc.createElement('style');
styleEl.type = 'text/css';
styleEl.id = 'react-draggable-style-el';
// Add nonce attribute for CSP compliance if provided
if (nonce) {
styleEl.setAttribute('nonce', nonce);
}
styleEl.innerHTML = '.react-draggable-transparent-selection *::-moz-selection {all: inherit;}\n';
styleEl.innerHTML += '.react-draggable-transparent-selection *::selection {all: inherit;}\n';
doc.getElementsByTagName('head')[0].appendChild(styleEl);
Expand Down
42 changes: 42 additions & 0 deletions test/DraggableCore.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,48 @@ describe('DraggableCore', () => {
});
});


it('should add nonce attribute to style element when nonce prop is provided', () => {
const testNonce = 'test-nonce-12345';
const { container } = render(
<DraggableCoreWrapper nonce={testNonce}>
<div />
</DraggableCoreWrapper>
);

// Clean up any existing style element
const existingStyle = document.getElementById('react-draggable-style-el');
if (existingStyle) existingStyle.remove();

startDrag(container.firstChild, { x: 0, y: 0 });

const styleEl = document.getElementById('react-draggable-style-el');
expect(styleEl).toBeTruthy();
expect(styleEl.getAttribute('nonce')).toBe(testNonce);

endDrag(container.firstChild, { x: 0, y: 0 });
});

it('should not add nonce attribute when nonce prop is not provided', () => {
const { container } = render(
<DraggableCoreWrapper>
<div />
</DraggableCoreWrapper>
);

// Clean up any existing style element
const existingStyle = document.getElementById('react-draggable-style-el');
if (existingStyle) existingStyle.remove();

startDrag(container.firstChild, { x: 0, y: 0 });

const styleEl = document.getElementById('react-draggable-style-el');
expect(styleEl).toBeTruthy();
expect(styleEl.getAttribute('nonce')).toBeNull();

endDrag(container.firstChild, { x: 0, y: 0 });
});

describe('unmount safety', () => {
it('should track mounted state correctly', () => {
const coreRef = React.createRef();
Expand Down
3 changes: 2 additions & 1 deletion typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ export interface DraggableCoreProps {
onDrag: DraggableEventHandler,
onStop: DraggableEventHandler,
onMouseDown: (e: MouseEvent) => void,
scale: number
scale: number,
nonce?: string
}

export default class Draggable extends React.Component<Partial<DraggableProps>, {}> {
Expand Down