@instructure/ui-drilldown 10.18.2-snapshot-1 → 10.18.2-snapshot-3

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
@@ -3,9 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [10.18.2-snapshot-1](https://github.com/instructure/instructure-ui/compare/v10.18.1...v10.18.2-snapshot-1) (2025-06-03)
6
+ ## [10.18.2-snapshot-3](https://github.com/instructure/instructure-ui/compare/v10.18.1...v10.18.2-snapshot-3) (2025-06-03)
7
7
 
8
- **Note:** Version bump only for package @instructure/ui-drilldown
8
+
9
+ ### Bug Fixes
10
+
11
+ * **many:** fix "not a valid selector" exception when an option ID contains quotes ([78e0b96](https://github.com/instructure/instructure-ui/commit/78e0b96edf29f3d476ba30b03134f1726bbdd0f4))
9
12
 
10
13
 
11
14
 
@@ -95,6 +95,28 @@ describe('<Drilldown />', () => {
95
95
  expect(options.length).toBe(0);
96
96
  expect(notAllowedChild).not.toBeInTheDocument();
97
97
  });
98
+ it('should not crash for weird option ids', async () => {
99
+ const onSelect = vi.fn();
100
+ const weirdID = 'some"_weird!@#$%^&*()\\|`id';
101
+ render(_jsx(Drilldown, {
102
+ rootPageId: "page0",
103
+ onSelect: onSelect,
104
+ children: _jsx(Drilldown.Page, {
105
+ id: "page0",
106
+ children: data.map(option => _jsx(Drilldown.Option, {
107
+ id: weirdID + option.id,
108
+ value: weirdID + option.id,
109
+ "data-testid": weirdID + option.id,
110
+ children: option.label
111
+ }, weirdID + option.id))
112
+ })
113
+ }));
114
+ const option_1 = screen.getByTestId(weirdID + 'opt_1');
115
+ await userEvent.click(option_1);
116
+ await waitFor(() => {
117
+ expect(onSelect).toHaveBeenCalled();
118
+ });
119
+ });
98
120
  });
99
121
  describe('id prop', () => {
100
122
  it('should put id attr on the drilldown', async () => {
@@ -259,7 +259,7 @@ let Drilldown = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle,
259
259
  }
260
260
  if (event.type === 'keydown' && href) {
261
261
  var _this$_drilldownRef;
262
- const optionEl = (_this$_drilldownRef = this._drilldownRef) === null || _this$_drilldownRef === void 0 ? void 0 : _this$_drilldownRef.querySelector(`#${id}`);
262
+ const optionEl = (_this$_drilldownRef = this._drilldownRef) === null || _this$_drilldownRef === void 0 ? void 0 : _this$_drilldownRef.querySelector(`#${CSS.escape(id)}`);
263
263
  const isLink = optionEl.tagName.toLowerCase() === 'a';
264
264
 
265
265
  // we need this check because in some cases
@@ -659,7 +659,7 @@ let Drilldown = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle,
659
659
  }
660
660
  focusOption(id) {
661
661
  const container = this._containerElement;
662
- const optionElement = container === null || container === void 0 ? void 0 : container.querySelector(`[id="${id}"]`);
662
+ const optionElement = container === null || container === void 0 ? void 0 : container.querySelector(`[id="${CSS.escape(id)}"]`);
663
663
  optionElement === null || optionElement === void 0 ? void 0 : optionElement.focus();
664
664
  }
665
665
  handleGroupOptionSelected(event, selectedOption) {
@@ -1190,7 +1190,7 @@ let Drilldown = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle,
1190
1190
  // Use action ID if exists, otherwise first non-action option's ID
1191
1191
  const targetId = actionLabel ? this._headerActionId : (_this$currentPage$chi2 = this.currentPage.children[0]) === null || _this$currentPage$chi2 === void 0 ? void 0 : _this$currentPage$chi2.props.id;
1192
1192
  if (!targetId) return null;
1193
- return (_this$_popover2 = this._popover) === null || _this$_popover2 === void 0 ? void 0 : (_this$_popover2$_cont = _this$_popover2._contentElement) === null || _this$_popover2$_cont === void 0 ? void 0 : _this$_popover2$_cont.querySelector(`#${targetId}`);
1193
+ return (_this$_popover2 = this._popover) === null || _this$_popover2 === void 0 ? void 0 : (_this$_popover2$_cont = _this$_popover2._contentElement) === null || _this$_popover2$_cont === void 0 ? void 0 : _this$_popover2$_cont.querySelector(`#${CSS.escape(targetId)}`);
1194
1194
  },
1195
1195
  elementRef: element => {
1196
1196
  // setting ref for "Popover" version, the popover root
@@ -96,6 +96,28 @@ describe('<Drilldown />', () => {
96
96
  expect(options.length).toBe(0);
97
97
  expect(notAllowedChild).not.toBeInTheDocument();
98
98
  });
99
+ it('should not crash for weird option ids', async () => {
100
+ const onSelect = _vitest.vi.fn();
101
+ const weirdID = 'some"_weird!@#$%^&*()\\|`id';
102
+ (0, _react.render)((0, _jsxRuntime.jsx)(_index.Drilldown, {
103
+ rootPageId: "page0",
104
+ onSelect: onSelect,
105
+ children: (0, _jsxRuntime.jsx)(_index.Drilldown.Page, {
106
+ id: "page0",
107
+ children: data.map(option => (0, _jsxRuntime.jsx)(_index.Drilldown.Option, {
108
+ id: weirdID + option.id,
109
+ value: weirdID + option.id,
110
+ "data-testid": weirdID + option.id,
111
+ children: option.label
112
+ }, weirdID + option.id))
113
+ })
114
+ }));
115
+ const option_1 = _react.screen.getByTestId(weirdID + 'opt_1');
116
+ await _userEvent.default.click(option_1);
117
+ await (0, _react.waitFor)(() => {
118
+ expect(onSelect).toHaveBeenCalled();
119
+ });
120
+ });
99
121
  });
100
122
  describe('id prop', () => {
101
123
  it('should put id attr on the drilldown', async () => {
@@ -268,7 +268,7 @@ let Drilldown = exports.Drilldown = (_dec = (0, _withDeterministicId.withDetermi
268
268
  }
269
269
  if (event.type === 'keydown' && href) {
270
270
  var _this$_drilldownRef;
271
- const optionEl = (_this$_drilldownRef = this._drilldownRef) === null || _this$_drilldownRef === void 0 ? void 0 : _this$_drilldownRef.querySelector(`#${id}`);
271
+ const optionEl = (_this$_drilldownRef = this._drilldownRef) === null || _this$_drilldownRef === void 0 ? void 0 : _this$_drilldownRef.querySelector(`#${CSS.escape(id)}`);
272
272
  const isLink = optionEl.tagName.toLowerCase() === 'a';
273
273
 
274
274
  // we need this check because in some cases
@@ -668,7 +668,7 @@ let Drilldown = exports.Drilldown = (_dec = (0, _withDeterministicId.withDetermi
668
668
  }
669
669
  focusOption(id) {
670
670
  const container = this._containerElement;
671
- const optionElement = container === null || container === void 0 ? void 0 : container.querySelector(`[id="${id}"]`);
671
+ const optionElement = container === null || container === void 0 ? void 0 : container.querySelector(`[id="${CSS.escape(id)}"]`);
672
672
  optionElement === null || optionElement === void 0 ? void 0 : optionElement.focus();
673
673
  }
674
674
  handleGroupOptionSelected(event, selectedOption) {
@@ -1199,7 +1199,7 @@ let Drilldown = exports.Drilldown = (_dec = (0, _withDeterministicId.withDetermi
1199
1199
  // Use action ID if exists, otherwise first non-action option's ID
1200
1200
  const targetId = actionLabel ? this._headerActionId : (_this$currentPage$chi2 = this.currentPage.children[0]) === null || _this$currentPage$chi2 === void 0 ? void 0 : _this$currentPage$chi2.props.id;
1201
1201
  if (!targetId) return null;
1202
- return (_this$_popover2 = this._popover) === null || _this$_popover2 === void 0 ? void 0 : (_this$_popover2$_cont = _this$_popover2._contentElement) === null || _this$_popover2$_cont === void 0 ? void 0 : _this$_popover2$_cont.querySelector(`#${targetId}`);
1202
+ return (_this$_popover2 = this._popover) === null || _this$_popover2 === void 0 ? void 0 : (_this$_popover2$_cont = _this$_popover2._contentElement) === null || _this$_popover2$_cont === void 0 ? void 0 : _this$_popover2$_cont.querySelector(`#${CSS.escape(targetId)}`);
1203
1203
  },
1204
1204
  elementRef: element => {
1205
1205
  // setting ref for "Popover" version, the popover root
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/ui-drilldown",
3
- "version": "10.18.2-snapshot-1",
3
+ "version": "10.18.2-snapshot-3",
4
4
  "description": "A drilldown menu component",
5
5
  "author": "Instructure, Inc. Engineering and Product Design",
6
6
  "module": "./es/index.js",
@@ -24,29 +24,29 @@
24
24
  "license": "MIT",
25
25
  "dependencies": {
26
26
  "@babel/runtime": "^7.26.0",
27
- "@instructure/console": "10.18.2-snapshot-1",
28
- "@instructure/emotion": "10.18.2-snapshot-1",
29
- "@instructure/shared-types": "10.18.2-snapshot-1",
30
- "@instructure/ui-dom-utils": "10.18.2-snapshot-1",
31
- "@instructure/ui-icons": "10.18.2-snapshot-1",
32
- "@instructure/ui-options": "10.18.2-snapshot-1",
33
- "@instructure/ui-popover": "10.18.2-snapshot-1",
34
- "@instructure/ui-position": "10.18.2-snapshot-1",
35
- "@instructure/ui-prop-types": "10.18.2-snapshot-1",
36
- "@instructure/ui-react-utils": "10.18.2-snapshot-1",
37
- "@instructure/ui-selectable": "10.18.2-snapshot-1",
38
- "@instructure/ui-testable": "10.18.2-snapshot-1",
39
- "@instructure/ui-utils": "10.18.2-snapshot-1",
40
- "@instructure/ui-view": "10.18.2-snapshot-1",
27
+ "@instructure/console": "10.18.2-snapshot-3",
28
+ "@instructure/emotion": "10.18.2-snapshot-3",
29
+ "@instructure/shared-types": "10.18.2-snapshot-3",
30
+ "@instructure/ui-dom-utils": "10.18.2-snapshot-3",
31
+ "@instructure/ui-icons": "10.18.2-snapshot-3",
32
+ "@instructure/ui-options": "10.18.2-snapshot-3",
33
+ "@instructure/ui-popover": "10.18.2-snapshot-3",
34
+ "@instructure/ui-position": "10.18.2-snapshot-3",
35
+ "@instructure/ui-prop-types": "10.18.2-snapshot-3",
36
+ "@instructure/ui-react-utils": "10.18.2-snapshot-3",
37
+ "@instructure/ui-selectable": "10.18.2-snapshot-3",
38
+ "@instructure/ui-testable": "10.18.2-snapshot-3",
39
+ "@instructure/ui-utils": "10.18.2-snapshot-3",
40
+ "@instructure/ui-view": "10.18.2-snapshot-3",
41
41
  "prop-types": "^15.8.1"
42
42
  },
43
43
  "devDependencies": {
44
- "@instructure/ui-axe-check": "10.18.2-snapshot-1",
45
- "@instructure/ui-babel-preset": "10.18.2-snapshot-1",
46
- "@instructure/ui-color-utils": "10.18.2-snapshot-1",
47
- "@instructure/ui-scripts": "10.18.2-snapshot-1",
48
- "@instructure/ui-test-utils": "10.18.2-snapshot-1",
49
- "@instructure/ui-themes": "10.18.2-snapshot-1",
44
+ "@instructure/ui-axe-check": "10.18.2-snapshot-3",
45
+ "@instructure/ui-babel-preset": "10.18.2-snapshot-3",
46
+ "@instructure/ui-color-utils": "10.18.2-snapshot-3",
47
+ "@instructure/ui-scripts": "10.18.2-snapshot-3",
48
+ "@instructure/ui-test-utils": "10.18.2-snapshot-3",
49
+ "@instructure/ui-themes": "10.18.2-snapshot-3",
50
50
  "@testing-library/jest-dom": "^6.6.3",
51
51
  "@testing-library/react": "^16.0.1",
52
52
  "@testing-library/user-event": "^14.5.2",
@@ -104,6 +104,33 @@ describe('<Drilldown />', () => {
104
104
  expect(options.length).toBe(0)
105
105
  expect(notAllowedChild).not.toBeInTheDocument()
106
106
  })
107
+
108
+ it('should not crash for weird option ids', async () => {
109
+ const onSelect = vi.fn()
110
+ const weirdID = 'some"_weird!@#$%^&*()\\|`id'
111
+ render(
112
+ <Drilldown rootPageId="page0" onSelect={onSelect}>
113
+ <Drilldown.Page id="page0">
114
+ {data.map((option) => (
115
+ <Drilldown.Option
116
+ id={weirdID + option.id}
117
+ value={weirdID + option.id}
118
+ key={weirdID + option.id}
119
+ data-testid={weirdID + option.id}
120
+ >
121
+ {option.label}
122
+ </Drilldown.Option>
123
+ ))}
124
+ </Drilldown.Page>
125
+ </Drilldown>
126
+ )
127
+ const option_1 = screen.getByTestId(weirdID + 'opt_1')
128
+ await userEvent.click(option_1)
129
+
130
+ await waitFor(() => {
131
+ expect(onSelect).toHaveBeenCalled()
132
+ })
133
+ })
107
134
  })
108
135
 
109
136
  describe('id prop', () => {
@@ -585,9 +585,8 @@ class Drilldown extends Component<DrilldownProps, DrilldownState> {
585
585
 
586
586
  focusOption(id: string) {
587
587
  const container = this._containerElement
588
-
589
588
  const optionElement = container?.querySelector(
590
- `[id="${id}"]`
589
+ `[id="${CSS.escape(id)}"]`
591
590
  ) as HTMLSpanElement
592
591
 
593
592
  optionElement?.focus()
@@ -816,7 +815,7 @@ class Drilldown extends Component<DrilldownProps, DrilldownState> {
816
815
 
817
816
  if (event.type === 'keydown' && href) {
818
817
  const optionEl = this._drilldownRef?.querySelector(
819
- `#${id}`
818
+ `#${CSS.escape(id)}`
820
819
  ) as HTMLLinkElement
821
820
  const isLink = optionEl.tagName.toLowerCase() === 'a'
822
821
 
@@ -1516,7 +1515,9 @@ class Drilldown extends Component<DrilldownProps, DrilldownState> {
1516
1515
  : this.currentPage.children[0]?.props.id
1517
1516
 
1518
1517
  if (!targetId) return null
1519
- return this._popover?._contentElement?.querySelector(`#${targetId}`)
1518
+ return this._popover?._contentElement?.querySelector(
1519
+ `#${CSS.escape(targetId)}`
1520
+ )
1520
1521
  }}
1521
1522
  elementRef={(element) => {
1522
1523
  // setting ref for "Popover" version, the popover root