@gitlab/duo-ui 8.12.0 → 8.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [8.12.1](https://gitlab.com/gitlab-org/duo-ui/compare/v8.12.0...v8.12.1) (2025-04-09)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * make custom code buttons work when response html lacks code tag ([35ce601](https://gitlab.com/gitlab-org/duo-ui/commit/35ce60111be3494ba58a4da7dfe4f916cc488547))
7
+
1
8
  # [8.12.0](https://gitlab.com/gitlab-org/duo-ui/compare/v8.11.0...v8.12.0) (2025-04-07)
2
9
 
3
10
 
@@ -2,19 +2,49 @@ import { createButton } from './buttons_utils';
2
2
  import { createTooltip } from './tooltips_utils';
3
3
  import { checkClipboardPermissions } from './utils';
4
4
 
5
+ function _defineProperty(e, r, t) {
6
+ return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
7
+ value: t,
8
+ enumerable: !0,
9
+ configurable: !0,
10
+ writable: !0
11
+ }) : e[r] = t, e;
12
+ }
13
+ function _toPrimitive(t, r) {
14
+ if ("object" != typeof t || !t) return t;
15
+ var e = t[Symbol.toPrimitive];
16
+ if (void 0 !== e) {
17
+ var i = e.call(t, r || "default");
18
+ if ("object" != typeof i) return i;
19
+ throw new TypeError("@@toPrimitive must return a primitive value.");
20
+ }
21
+ return ("string" === r ? String : Number)(t);
22
+ }
23
+ function _toPropertyKey(t) {
24
+ var i = _toPrimitive(t, "string");
25
+ return "symbol" == typeof i ? i : i + "";
26
+ }
27
+
5
28
  class CopyCodeElement extends HTMLElement {
6
29
  constructor() {
7
30
  super();
31
+ _defineProperty(this, "getCodeElement", () => {
32
+ const wrapper = this.parentNode;
33
+ return wrapper.getElementsByTagName('code')[0] || wrapper.getElementsByTagName('pre')[0];
34
+ });
8
35
  this.initialize();
9
36
  }
10
37
  async initialize() {
38
+ if (!this.getCodeElement()) return;
11
39
  const btn = createButton('Copy to clipboard', 'copy-to-clipboard');
12
40
  this.appendChild(btn);
13
41
  const tooltip = await createTooltip(btn, 'Copy to clipboard');
14
42
  this.appendChild(tooltip);
15
- const wrapper = this.parentNode;
16
- const [codeElement] = wrapper.getElementsByTagName('code');
17
43
  btn.addEventListener('click', async () => {
44
+ // We fetch the code element on click instead because the structure of the HTML can change after
45
+ // the element is initialized (e.g. on sanitation), and we don't want to use an element that is
46
+ // not present in the DOM.
47
+ const codeElement = this.getCodeElement();
18
48
  const textToCopy = codeElement.innerText;
19
49
  const hasClipboardPermission = await checkClipboardPermissions();
20
50
  try {
@@ -17,55 +17,80 @@ function _classPrivateFieldInitSpec(e, t, a) {
17
17
  function _classPrivateFieldSet2(s, a, r) {
18
18
  return s.set(_assertClassBrand(s, a), r), r;
19
19
  }
20
- function _toSetter(t, e, n) {
21
- e || (e = []);
22
- var r = e.length++;
23
- return Object.defineProperty({}, "_", {
24
- set: function (o) {
25
- e[r] = o, t.apply(n, e);
26
- }
27
- });
20
+ function _defineProperty(e, r, t) {
21
+ return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
22
+ value: t,
23
+ enumerable: !0,
24
+ configurable: !0,
25
+ writable: !0
26
+ }) : e[r] = t, e;
27
+ }
28
+ function _toPrimitive(t, r) {
29
+ if ("object" != typeof t || !t) return t;
30
+ var e = t[Symbol.toPrimitive];
31
+ if (void 0 !== e) {
32
+ var i = e.call(t, r || "default");
33
+ if ("object" != typeof i) return i;
34
+ throw new TypeError("@@toPrimitive must return a primitive value.");
35
+ }
36
+ return ("string" === r ? String : Number)(t);
37
+ }
38
+ function _toPropertyKey(t) {
39
+ var i = _toPrimitive(t, "string");
40
+ return "symbol" == typeof i ? i : i + "";
28
41
  }
29
42
 
30
43
  const CODE_MARKDOWN_CLASS = 'js-markdown-code';
31
44
  var _actionButton = /*#__PURE__*/new WeakMap();
32
- var _codeElement = /*#__PURE__*/new WeakMap();
45
+ var _codeBlock = /*#__PURE__*/new WeakMap();
33
46
  var _handleClick = /*#__PURE__*/new WeakMap();
34
47
  class InsertCodeSnippetElement extends HTMLElement {
35
- constructor(codeBlock) {
48
+ constructor(_codeBlock2) {
36
49
  super();
37
50
  _classPrivateFieldInitSpec(this, _actionButton, void 0);
38
- _classPrivateFieldInitSpec(this, _codeElement, void 0);
51
+ _classPrivateFieldInitSpec(this, _codeBlock, void 0);
39
52
  _classPrivateFieldInitSpec(this, _handleClick, () => {
40
- if (_classPrivateFieldGet2(_codeElement, this)) {
41
- _classPrivateFieldGet2(_codeElement, this).dispatchEvent(new CustomEvent('insert-code-snippet', {
53
+ // We fetch the code element on click instead because the structure of the HTML can change after
54
+ // the element is initialized (e.g. on sanitation), and we don't want to use an element that is
55
+ // not present in the DOM.
56
+ const codeElement = this.getCodeElement(_classPrivateFieldGet2(_codeBlock, this));
57
+ if (codeElement) {
58
+ codeElement.dispatchEvent(new CustomEvent('insert-code-snippet', {
42
59
  bubbles: true,
43
60
  cancelable: true,
44
61
  detail: {
45
- code: _classPrivateFieldGet2(_codeElement, this).textContent.trim()
62
+ code: codeElement.textContent.trim()
46
63
  }
47
64
  }));
48
65
  }
49
66
  });
50
- this.initialize(codeBlock);
67
+ _defineProperty(this, "getCodeElement", codeBlock => {
68
+ const wrapper = codeBlock !== null && codeBlock !== void 0 ? codeBlock : this.closest(`.${CODE_MARKDOWN_CLASS}`);
69
+ return wrapper.getElementsByTagName('code')[0] || wrapper.getElementsByTagName('pre')[0];
70
+ });
71
+ this.initialize(_codeBlock2);
51
72
  }
73
+
74
+ // we handle two possible cases here:
75
+ // 1. we use constructor parameter if the element is created in Javascript and inserted in the document
76
+ // 2. we find the wrapping element containing code if the element is received from the server
52
77
  async initialize(codeBlock) {
78
+ if (!this.getCodeElement(codeBlock)) return;
79
+ _classPrivateFieldSet2(_codeBlock, this, codeBlock);
53
80
  _classPrivateFieldSet2(_actionButton, this, createButton());
54
-
55
- // we handle two possible cases here:
56
- // 1. we use constructor parameter if the element is created in Javscript and inserted in the document
57
- // 2. we find the wrapping element containing code if the element is received from the server
58
- const wrapper = codeBlock !== null && codeBlock !== void 0 ? codeBlock : this.closest(`.${CODE_MARKDOWN_CLASS}`);
59
- [_toSetter(_classPrivateFieldSet2, [_codeElement, this])._] = wrapper.getElementsByTagName('code');
60
81
  const tooltip = await createTooltip(_classPrivateFieldGet2(_actionButton, this), 'Insert at cursor');
61
82
  this.appendChild(tooltip);
62
83
  }
63
84
  connectedCallback() {
64
- this.appendChild(_classPrivateFieldGet2(_actionButton, this));
65
- _classPrivateFieldGet2(_actionButton, this).addEventListener('click', _classPrivateFieldGet2(_handleClick, this));
85
+ if (_classPrivateFieldGet2(_actionButton, this)) {
86
+ this.appendChild(_classPrivateFieldGet2(_actionButton, this));
87
+ _classPrivateFieldGet2(_actionButton, this).addEventListener('click', _classPrivateFieldGet2(_handleClick, this));
88
+ }
66
89
  }
67
90
  disconnectedCallback() {
68
- _classPrivateFieldGet2(_actionButton, this).removeEventListener('click', _classPrivateFieldGet2(_handleClick, this));
91
+ if (_classPrivateFieldGet2(_actionButton, this)) {
92
+ _classPrivateFieldGet2(_actionButton, this).removeEventListener('click', _classPrivateFieldGet2(_handleClick, this));
93
+ }
69
94
  }
70
95
  }
71
96
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/duo-ui",
3
- "version": "8.12.0",
3
+ "version": "8.12.1",
4
4
  "description": "Duo UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -9,16 +9,19 @@ export class CopyCodeElement extends HTMLElement {
9
9
  }
10
10
 
11
11
  async initialize() {
12
+ if (!this.getCodeElement()) return;
13
+
12
14
  const btn = createButton('Copy to clipboard', 'copy-to-clipboard');
13
15
  this.appendChild(btn);
14
16
 
15
17
  const tooltip = await createTooltip(btn, 'Copy to clipboard');
16
18
  this.appendChild(tooltip);
17
19
 
18
- const wrapper = this.parentNode;
19
- const [codeElement] = wrapper.getElementsByTagName('code');
20
-
21
20
  btn.addEventListener('click', async () => {
21
+ // We fetch the code element on click instead because the structure of the HTML can change after
22
+ // the element is initialized (e.g. on sanitation), and we don't want to use an element that is
23
+ // not present in the DOM.
24
+ const codeElement = this.getCodeElement();
22
25
  const textToCopy = codeElement.innerText;
23
26
  const hasClipboardPermission = await checkClipboardPermissions();
24
27
 
@@ -42,4 +45,9 @@ export class CopyCodeElement extends HTMLElement {
42
45
  }
43
46
  });
44
47
  }
48
+
49
+ getCodeElement = () => {
50
+ const wrapper = this.parentNode;
51
+ return wrapper.getElementsByTagName('code')[0] || wrapper.getElementsByTagName('pre')[0];
52
+ };
45
53
  }
@@ -6,34 +6,38 @@ const CODE_MARKDOWN_CLASS = 'js-markdown-code';
6
6
  export class InsertCodeSnippetElement extends HTMLElement {
7
7
  #actionButton;
8
8
 
9
- #codeElement;
9
+ #codeBlock;
10
10
 
11
11
  constructor(codeBlock) {
12
12
  super();
13
13
  this.initialize(codeBlock);
14
14
  }
15
15
 
16
+ // we handle two possible cases here:
17
+ // 1. we use constructor parameter if the element is created in Javascript and inserted in the document
18
+ // 2. we find the wrapping element containing code if the element is received from the server
16
19
  async initialize(codeBlock) {
17
- this.#actionButton = createButton();
20
+ if (!this.getCodeElement(codeBlock)) return;
18
21
 
19
- // we handle two possible cases here:
20
- // 1. we use constructor parameter if the element is created in Javscript and inserted in the document
21
- // 2. we find the wrapping element containing code if the element is received from the server
22
- const wrapper = codeBlock ?? this.closest(`.${CODE_MARKDOWN_CLASS}`);
23
- [this.#codeElement] = wrapper.getElementsByTagName('code');
22
+ this.#codeBlock = codeBlock;
23
+ this.#actionButton = createButton();
24
24
 
25
25
  const tooltip = await createTooltip(this.#actionButton, 'Insert at cursor');
26
26
  this.appendChild(tooltip);
27
27
  }
28
28
 
29
29
  #handleClick = () => {
30
- if (this.#codeElement) {
31
- this.#codeElement.dispatchEvent(
30
+ // We fetch the code element on click instead because the structure of the HTML can change after
31
+ // the element is initialized (e.g. on sanitation), and we don't want to use an element that is
32
+ // not present in the DOM.
33
+ const codeElement = this.getCodeElement(this.#codeBlock);
34
+ if (codeElement) {
35
+ codeElement.dispatchEvent(
32
36
  new CustomEvent('insert-code-snippet', {
33
37
  bubbles: true,
34
38
  cancelable: true,
35
39
  detail: {
36
- code: this.#codeElement.textContent.trim(),
40
+ code: codeElement.textContent.trim(),
37
41
  },
38
42
  })
39
43
  );
@@ -41,11 +45,20 @@ export class InsertCodeSnippetElement extends HTMLElement {
41
45
  };
42
46
 
43
47
  connectedCallback() {
44
- this.appendChild(this.#actionButton);
45
- this.#actionButton.addEventListener('click', this.#handleClick);
48
+ if (this.#actionButton) {
49
+ this.appendChild(this.#actionButton);
50
+ this.#actionButton.addEventListener('click', this.#handleClick);
51
+ }
46
52
  }
47
53
 
48
54
  disconnectedCallback() {
49
- this.#actionButton.removeEventListener('click', this.#handleClick);
55
+ if (this.#actionButton) {
56
+ this.#actionButton.removeEventListener('click', this.#handleClick);
57
+ }
50
58
  }
59
+
60
+ getCodeElement = (codeBlock) => {
61
+ const wrapper = codeBlock ?? this.closest(`.${CODE_MARKDOWN_CLASS}`);
62
+ return wrapper.getElementsByTagName('code')[0] || wrapper.getElementsByTagName('pre')[0];
63
+ };
51
64
  }