ngwr 14.5.0 → 14.6.0
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/README.md +9 -3
- package/avatar/styles/_index.scss +46 -0
- package/fesm2022/ngwr-avatar.mjs +58 -7
- package/fesm2022/ngwr-avatar.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +16 -5
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n.mjs +13 -0
- package/fesm2022/ngwr-i18n.mjs.map +1 -1
- package/fesm2022/ngwr-image-cropper-testing.mjs +34 -3
- package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -1
- package/fesm2022/ngwr-image-cropper.mjs +98 -5
- package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
- package/fesm2022/ngwr-pagination.mjs +1 -1
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-schema-form.mjs +1 -1
- package/fesm2022/ngwr-schema-form.mjs.map +1 -1
- package/fesm2022/ngwr-select-testing.mjs +21 -2
- package/fesm2022/ngwr-select-testing.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +233 -35
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-table.mjs +10 -2
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/image-cropper/styles/_index.scss +7 -0
- package/llms-full.txt +5 -5
- package/llms.txt +12 -3
- package/mcp/providers.js +8 -2
- package/package.json +1 -1
- package/schematics/migrations/v14/index.js +90 -34
- package/schematics/migrations/v7/index.js +15 -4
- package/schematics/migrations/v8/index.js +13 -2
- package/schematics/migrations/v9/index.js +19 -10
- package/schematics/use/symbol-map.json +1 -0
- package/select/styles/_index.scss +34 -0
- package/skills/ngwr/SKILL.md +9 -4
- package/skills/ngwr/references/catalog.md +1 -1
- package/skills/ngwr/references/setup.md +2 -2
- package/table/styles/_index.scss +39 -12
- package/types/ngwr-avatar.d.ts +36 -3
- package/types/ngwr-i18n-en.d.ts +16 -5
- package/types/ngwr-i18n.d.ts +13 -0
- package/types/ngwr-image-cropper-testing.d.ts +25 -3
- package/types/ngwr-image-cropper.d.ts +88 -2
- package/types/ngwr-select-testing.d.ts +18 -1
- package/types/ngwr-select.d.ts +181 -10
- package/types/ngwr-table.d.ts +8 -0
- package/types/ngwr-version.d.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-select-testing.mjs","sources":["../../../projects/lib/select/testing/wr-option-harness.ts","../../../projects/lib/select/testing/wr-select-harness.ts","../../../projects/lib/select/testing/ngwr-select-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrOptionHarnessFilters } from './interfaces';\n\n/**\n * Test harness for one option inside a `<wr-select>` panel.\n *\n * The selector is the CLASS, not the `wr-option` element: a select rendering a\n * virtualized list emits plain `<div role=\"option\" class=\"wr-option\">` rows\n * instead of components, and a spec should not have to care which one it got.\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrOptionHarness extends ComponentHarness {\n /** The projected component and the virtualized row both carry this class. */\n static hostSelector = '.wr-option';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrOptionHarnessFilters = {}): HarnessPredicate<WrOptionHarness> {\n return new HarnessPredicate(WrOptionHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption('selected', options.selected, async (harness, selected) => (await harness.isSelected()) === selected)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n /** The option's label, trimmed. */\n async getText(): Promise<string> {\n return (await this.host()).text();\n }\n\n /** Whether the option is currently selected. */\n async isSelected(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-selected')) === 'true';\n }\n\n /** Whether the option refuses selection. */\n async isDisabled(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-disabled')) === 'true';\n }\n\n /** Whether the option is the keyboard cursor target. */\n async isActive(): Promise<boolean> {\n return (await this.host()).hasClass('wr-option--active');\n }\n\n /**\n * Whether a search query has filtered this option out.\n *\n * Filtered options stay in the DOM — registration order has to survive a\n * query — and collapse via CSS, so a spec that counts options while a search\n * is active gets them back unless it asks. `getOptions()` on the select\n * harness already drops them.\n */\n async isHidden(): Promise<boolean> {\n return (await this.host()).hasClass('wr-option--hidden');\n }\n\n /** Click the option. */\n async click(): Promise<void> {\n return (await this.host()).click();\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport {\n ComponentHarness,\n type HarnessLoader,\n HarnessPredicate,\n TestKey,\n type TestElement,\n} from '@angular/cdk/testing';\n\nimport type { WrOptionHarnessFilters, WrSelectHarnessFilters } from './interfaces';\nimport { WrOptionHarness } from './wr-option-harness';\n\n/**\n * Test harness for `<wr-select>` — single, multi, tag and search modes.\n *\n * The panel is NOT inside the select: it is a template portal in the overlay\n * container, a sibling of the whole app. So the options are reached through the\n * document root, scoped by the `aria-controls` id the trigger already publishes\n * — which is what keeps two selects on one page from reading each other's\n * options.\n *\n * @example\n * ```ts\n * const select = await loader.getHarness(WrSelectHarness);\n *\n * await select.open();\n * await select.selectOption({ text: 'Medium' });\n *\n * expect(await select.getValueText()).toBe('Medium');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrSelectHarness extends ComponentHarness {\n static hostSelector = 'wr-select';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrSelectHarnessFilters = {}): HarnessPredicate<WrSelectHarness> {\n return new HarnessPredicate(WrSelectHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getValueText(), text))\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)\n .addOption('open', options.open, async (harness, open) => (await harness.isOpen()) === open);\n }\n\n /** Whether the panel is showing. */\n async isOpen(): Promise<boolean> {\n return (await this.host()).hasClass('wr-select--open');\n }\n\n /** Whether the select refuses interaction. */\n async isDisabled(): Promise<boolean> {\n return (await this.host()).hasClass('wr-select--disabled');\n }\n\n /** Whether the select takes more than one value (multi or tag mode). */\n async isMultiple(): Promise<boolean> {\n return (await this.host()).hasClass('wr-select--multi');\n }\n\n /**\n * What the trigger currently shows as the selection: the chip labels joined by\n * `', '` in multi and tag modes, the label in single mode, the input's text in\n * search mode, and `''` when nothing is selected (the placeholder is showing).\n */\n async getValueText(): Promise<string> {\n const chips = await this.getChipLabels();\n if (chips.length > 0) return chips.join(', ');\n\n const value = await this.locatorForOptional('.wr-select__value')();\n if (value) return value.text();\n\n // Search mode keeps the display text in the input's value, not in the DOM.\n const search = await this.locatorForOptional('.wr-select__search-input')();\n return search ? search.getProperty<string>('value') : '';\n }\n\n /** The placeholder, or `null` when a selection is hiding it. */\n async getPlaceholder(): Promise<string | null> {\n const placeholder = await this.locatorForOptional('.wr-select__placeholder')();\n if (placeholder) return placeholder.text();\n\n const input = await this.searchInput();\n return input ? input.getProperty<string>('placeholder') : null;\n }\n\n /** The visible chip labels, in order. The `+N more` overflow chip is not one. */\n async getChipLabels(): Promise<string[]> {\n const labels = await this.locatorForAll('.wr-select__chip-label')();\n return Promise.all(labels.map(label => label.text()));\n }\n\n /**\n * Open the panel.\n *\n * Throws rather than resolving quietly when the panel does not appear, because\n * the two reasons it would not are both worth failing on: a tag-mode select\n * has no panel at all, and a searchable one with `minChars` opens on the query\n * rather than on focus.\n */\n async open(): Promise<void> {\n if (await this.isOpen()) return;\n\n await (await this.locatorFor('.wr-select__trigger')()).click();\n if (await this.isOpen()) return;\n\n throw new Error(\n 'WrSelectHarness.open(): the panel did not open. A tag-mode select has no panel, and a ' +\n 'searchable select with `minChars` opens on the query — call typeSearch() instead.'\n );\n }\n\n /** Close the panel. A closed select is left alone. */\n async close(): Promise<void> {\n if (!(await this.isOpen())) return;\n // Escape reaches the select through the CDK's keyboard dispatcher, so it\n // works for the button trigger and the search input alike.\n await (await this.locatorFor('.wr-select__trigger')()).sendKeys(TestKey.ESCAPE);\n }\n\n /**\n * The options currently offered, in DOM order.\n *\n * Filtered-out options are dropped: a client-side search leaves them in the\n * DOM (registration order has to survive a query) and collapses them with CSS,\n * so counting raw elements would answer with the unfiltered list.\n */\n async getOptions(filters: WrOptionHarnessFilters = {}): Promise<WrOptionHarness[]> {\n const loader = await this.panelLoader();\n const all = await loader.getAllHarnesses(WrOptionHarness.with(filters));\n\n const visible: WrOptionHarness[] = [];\n for (const option of all) {\n if (!(await option.isHidden())) visible.push(option);\n }\n return visible;\n }\n\n /** The labels of the options currently offered, in DOM order. */\n async getOptionLabels(): Promise<string[]> {\n const options = await this.getOptions();\n return Promise.all(options.map(option => option.getText()));\n }\n\n /** Open the panel if needed, then click the first option matching the filters. */\n async selectOption(filters: WrOptionHarnessFilters): Promise<void> {\n await this.open();\n\n const [option] = await this.getOptions(filters);\n if (!option) throw new Error(`WrSelectHarness.selectOption(): no option matched ${JSON.stringify(filters)}.`);\n\n await option.click();\n }\n\n /** Type into a search or tag select's input, replacing whatever is there. */\n async typeSearch(query: string): Promise<void> {\n const input = await this.searchInput();\n if (!input) throw new Error('WrSelectHarness.typeSearch(): this select has no text input.');\n\n await input.clear();\n if (query) await input.sendKeys(query);\n }\n\n /** Click the clear (×) control. It only exists on a `clearable` select with a selection. */\n async clear(): Promise<void> {\n await (await this.locatorFor('.wr-select__clear')()).click();\n }\n\n /** Remove one chip by its label. */\n async removeChip(label: string): Promise<void> {\n const labels = await this.getChipLabels();\n const index = labels.indexOf(label);\n if (index < 0) throw new Error(`WrSelectHarness.removeChip(): no chip labelled \"${label}\".`);\n\n // Label and remove control are emitted once per chip by the same loop, so\n // the two lists line up by index.\n const removes = await this.locatorForAll('.wr-select__chip-remove')();\n await removes[index].click();\n }\n\n /** The \"no results\" message, or `null` when the panel is showing options. */\n async getNoResultsText(): Promise<string | null> {\n const empty = await this.inPanel('.wr-select-panel__empty');\n return empty ? empty.text() : null;\n }\n\n /** Whether the panel is showing its async loading row. */\n async isLoading(): Promise<boolean> {\n return (await this.inPanel('.wr-select-panel__loading')) !== null;\n }\n\n /** Move keyboard focus to the select. */\n async focus(): Promise<void> {\n const input = await this.searchInput();\n return (input ?? (await this.locatorFor('.wr-select__trigger')())).focus();\n }\n\n /** The text input of a search or tag select, or `null` for the button trigger. */\n private async searchInput(): Promise<TestElement | null> {\n return this.locatorForOptional('.wr-select__search-input, .wr-select__tag-input')();\n }\n\n /** One element inside THIS select's panel, or `null`. */\n private async inPanel(selector: string): Promise<TestElement | null> {\n return this.documentRootLocatorFactory().locatorForOptional(`#${await this.panelId()} ${selector}`)();\n }\n\n /**\n * A loader scoped to THIS select's panel.\n *\n * Scoped by the `aria-controls` id rather than by `.wr-select-panel`: the\n * overlay container is shared, so a bare class query would happily return the\n * options of a different select that happens to be open.\n */\n private async panelLoader(): Promise<HarnessLoader> {\n return this.documentRootLocatorFactory().harnessLoaderFor(`#${await this.panelId()}`);\n }\n\n /** The id the trigger publishes as `aria-controls`, which the panel carries. */\n private async panelId(): Promise<string> {\n const combobox = await this.locatorForOptional('[role=\"combobox\"]')();\n const id = combobox ? await combobox.getAttribute('aria-controls') : null;\n\n if (!id) {\n throw new Error('WrSelectHarness: this select has no panel — tag mode renders its options inline.');\n }\n if (!(await this.isOpen())) {\n throw new Error('WrSelectHarness: the panel is closed — call open() first.');\n }\n\n return id;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-select-testing.mjs","sources":["../../../projects/lib/select/testing/wr-option-harness.ts","../../../projects/lib/select/testing/wr-select-harness.ts","../../../projects/lib/select/testing/ngwr-select-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrOptionHarnessFilters } from './interfaces';\n\n/**\n * Test harness for one option inside a `<wr-select>` panel.\n *\n * The selector is the CLASS, not the `wr-option` element: a select rendering a\n * virtualized list emits plain `<div role=\"option\" class=\"wr-option\">` rows\n * instead of components, and a spec should not have to care which one it got.\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrOptionHarness extends ComponentHarness {\n /** The projected component and the virtualized row both carry this class. */\n static hostSelector = '.wr-option';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrOptionHarnessFilters = {}): HarnessPredicate<WrOptionHarness> {\n return new HarnessPredicate(WrOptionHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption('selected', options.selected, async (harness, selected) => (await harness.isSelected()) === selected)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n /**\n * The option's text as drawn, trimmed — without its leading visual.\n *\n * A `wrOptionLeading` template renders INSIDE the row, so reading the whole\n * host would hand back an avatar's initials as the first word of the name. It\n * is excluded here for the same reason the select excludes it from the label.\n *\n * This is the drawn text, not the `label` input: an option whose `label` is\n * shorter than what it draws still answers with what it draws. What the select\n * REPORTS for it is on the select harness, as `getValueText()` or\n * `getChipLabels()`.\n */\n async getText(): Promise<string> {\n return (await this.host()).text({ exclude: '.wr-option__leading' });\n }\n\n /**\n * Whether the row is drawing a leading visual right now. Reads the slot the\n * select renders the template into, so it answers `false` for an option that\n * declares a template while the panel is closed — which is the contract.\n */\n async hasLeading(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-option__leading')()) !== null;\n }\n\n /** Whether the option is currently selected. */\n async isSelected(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-selected')) === 'true';\n }\n\n /** Whether the option refuses selection. */\n async isDisabled(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-disabled')) === 'true';\n }\n\n /** Whether the option is the keyboard cursor target. */\n async isActive(): Promise<boolean> {\n return (await this.host()).hasClass('wr-option--active');\n }\n\n /**\n * Whether a search query has filtered this option out.\n *\n * Filtered options stay in the DOM — registration order has to survive a\n * query — and collapse via CSS, so a spec that counts options while a search\n * is active gets them back unless it asks. `getOptions()` on the select\n * harness already drops them.\n */\n async isHidden(): Promise<boolean> {\n return (await this.host()).hasClass('wr-option--hidden');\n }\n\n /** Click the option. */\n async click(): Promise<void> {\n return (await this.host()).click();\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport {\n ComponentHarness,\n type HarnessLoader,\n HarnessPredicate,\n TestKey,\n type TestElement,\n} from '@angular/cdk/testing';\n\nimport type { WrOptionHarnessFilters, WrSelectHarnessFilters } from './interfaces';\nimport { WrOptionHarness } from './wr-option-harness';\n\n/**\n * Test harness for `<wr-select>` — single, multi, tag and search modes.\n *\n * The panel is NOT inside the select: it is a template portal in the overlay\n * container, a sibling of the whole app. So the options are reached through the\n * document root, scoped by the `aria-controls` id the trigger already publishes\n * — which is what keeps two selects on one page from reading each other's\n * options.\n *\n * @example\n * ```ts\n * const select = await loader.getHarness(WrSelectHarness);\n *\n * await select.open();\n * await select.selectOption({ text: 'Medium' });\n *\n * expect(await select.getValueText()).toBe('Medium');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrSelectHarness extends ComponentHarness {\n static hostSelector = 'wr-select';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrSelectHarnessFilters = {}): HarnessPredicate<WrSelectHarness> {\n return new HarnessPredicate(WrSelectHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getValueText(), text))\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)\n .addOption('open', options.open, async (harness, open) => (await harness.isOpen()) === open);\n }\n\n /** Whether the panel is showing. */\n async isOpen(): Promise<boolean> {\n return (await this.host()).hasClass('wr-select--open');\n }\n\n /** Whether the select refuses interaction. */\n async isDisabled(): Promise<boolean> {\n return (await this.host()).hasClass('wr-select--disabled');\n }\n\n /** Whether the select takes more than one value (multi or tag mode). */\n async isMultiple(): Promise<boolean> {\n return (await this.host()).hasClass('wr-select--multi');\n }\n\n /**\n * What the trigger currently shows as the selection: the chip labels joined by\n * `', '` in multi and tag modes, the label in single mode, the input's text in\n * search mode, and `''` when nothing is selected (the placeholder is showing).\n */\n async getValueText(): Promise<string> {\n const chips = await this.getChipLabels();\n if (chips.length > 0) return chips.join(', ');\n\n const value = await this.locatorForOptional('.wr-select__value')();\n if (value) return value.text();\n\n // Search mode keeps the display text in the input's value, not in the DOM.\n const search = await this.locatorForOptional('.wr-select__search-input')();\n return search ? search.getProperty<string>('value') : '';\n }\n\n /** The placeholder, or `null` when a selection is hiding it. */\n async getPlaceholder(): Promise<string | null> {\n const placeholder = await this.locatorForOptional('.wr-select__placeholder')();\n if (placeholder) return placeholder.text();\n\n const input = await this.searchInput();\n return input ? input.getProperty<string>('placeholder') : null;\n }\n\n /** The visible chip labels, in order. The `+N more` overflow chip is not one. */\n async getChipLabels(): Promise<string[]> {\n const labels = await this.locatorForAll('.wr-select__chip-label')();\n return Promise.all(labels.map(label => label.text()));\n }\n\n /**\n * Open the panel.\n *\n * Throws rather than resolving quietly when the panel does not appear, because\n * the two reasons it would not are both worth failing on: a tag-mode select\n * has no panel at all, and a searchable one with `minChars` opens on the query\n * rather than on focus.\n */\n async open(): Promise<void> {\n if (await this.isOpen()) return;\n\n await (await this.locatorFor('.wr-select__trigger')()).click();\n if (await this.isOpen()) return;\n\n throw new Error(\n 'WrSelectHarness.open(): the panel did not open. A tag-mode select has no panel, and a ' +\n 'searchable select with `minChars` opens on the query — call typeSearch() instead.'\n );\n }\n\n /** Close the panel. A closed select is left alone. */\n async close(): Promise<void> {\n if (!(await this.isOpen())) return;\n // Escape reaches the select through the CDK's keyboard dispatcher, so it\n // works for the button trigger and the search input alike.\n await (await this.locatorFor('.wr-select__trigger')()).sendKeys(TestKey.ESCAPE);\n }\n\n /**\n * The options currently offered, in DOM order.\n *\n * Filtered-out options are dropped: a client-side search leaves them in the\n * DOM (registration order has to survive a query) and collapses them with CSS,\n * so counting raw elements would answer with the unfiltered list.\n */\n async getOptions(filters: WrOptionHarnessFilters = {}): Promise<WrOptionHarness[]> {\n const loader = await this.panelLoader();\n const all = await loader.getAllHarnesses(WrOptionHarness.with(filters));\n\n const visible: WrOptionHarness[] = [];\n for (const option of all) {\n if (!(await option.isHidden())) visible.push(option);\n }\n return visible;\n }\n\n /** The labels of the options currently offered, in DOM order. */\n async getOptionLabels(): Promise<string[]> {\n const options = await this.getOptions();\n return Promise.all(options.map(option => option.getText()));\n }\n\n /** Open the panel if needed, then click the first option matching the filters. */\n async selectOption(filters: WrOptionHarnessFilters): Promise<void> {\n await this.open();\n\n const [option] = await this.getOptions(filters);\n if (!option) throw new Error(`WrSelectHarness.selectOption(): no option matched ${JSON.stringify(filters)}.`);\n\n await option.click();\n }\n\n /** Type into a search or tag select's input, replacing whatever is there. */\n async typeSearch(query: string): Promise<void> {\n const input = await this.searchInput();\n if (!input) throw new Error('WrSelectHarness.typeSearch(): this select has no text input.');\n\n await input.clear();\n if (query) await input.sendKeys(query);\n }\n\n /** Click the clear (×) control. It only exists on a `clearable` select with a selection. */\n async clear(): Promise<void> {\n await (await this.locatorFor('.wr-select__clear')()).click();\n }\n\n /** Remove one chip by its label. */\n async removeChip(label: string): Promise<void> {\n const labels = await this.getChipLabels();\n const index = labels.indexOf(label);\n if (index < 0) throw new Error(`WrSelectHarness.removeChip(): no chip labelled \"${label}\".`);\n\n // Label and remove control are emitted once per chip by the same loop, so\n // the two lists line up by index.\n const removes = await this.locatorForAll('.wr-select__chip-remove')();\n await removes[index].click();\n }\n\n /** The \"no results\" message, or `null` when the panel is showing options. */\n async getNoResultsText(): Promise<string | null> {\n const empty = await this.inPanel('.wr-select-panel__empty');\n return empty ? empty.text() : null;\n }\n\n /** Whether the panel is showing its async loading row. */\n async isLoading(): Promise<boolean> {\n return (await this.inPanel('.wr-select-panel__loading')) !== null;\n }\n\n /** Move keyboard focus to the select. */\n async focus(): Promise<void> {\n const input = await this.searchInput();\n return (input ?? (await this.locatorFor('.wr-select__trigger')())).focus();\n }\n\n /** The text input of a search or tag select, or `null` for the button trigger. */\n private async searchInput(): Promise<TestElement | null> {\n return this.locatorForOptional('.wr-select__search-input, .wr-select__tag-input')();\n }\n\n /** One element inside THIS select's panel, or `null`. */\n private async inPanel(selector: string): Promise<TestElement | null> {\n return this.documentRootLocatorFactory().locatorForOptional(`#${await this.panelId()} ${selector}`)();\n }\n\n /**\n * A loader scoped to THIS select's panel.\n *\n * Scoped by the `aria-controls` id rather than by `.wr-select-panel`: the\n * overlay container is shared, so a bare class query would happily return the\n * options of a different select that happens to be open.\n */\n private async panelLoader(): Promise<HarnessLoader> {\n return this.documentRootLocatorFactory().harnessLoaderFor(`#${await this.panelId()}`);\n }\n\n /** The id the trigger publishes as `aria-controls`, which the panel carries. */\n private async panelId(): Promise<string> {\n const combobox = await this.locatorForOptional('[role=\"combobox\"]')();\n const id = combobox ? await combobox.getAttribute('aria-controls') : null;\n\n if (!id) {\n throw new Error('WrSelectHarness: this select has no panel — tag mode renders its options inline.');\n }\n if (!(await this.isOpen())) {\n throw new Error('WrSelectHarness: the panel is closed — call open() first.');\n }\n\n return id;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|