diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 00000000..927418f0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,103 @@ +name: Bug report +description: Something in Bitrix24 UI behaves differently from what it documents. +title: "fix(Component): " +labels: [bug] +body: + - type: markdown + attributes: + value: | + Before filing, please check the [open issues](https://github.com/bitrix24/b24ui/issues?q=is%3Aissue) — + this library is a fork of [Nuxt UI](https://github.com/nuxt/ui) and ports land continuously, + so a bug reported a week ago may already be fixed on `main`. + + Keep the title a [conventional commit](https://www.conventionalcommits.org/) subject + if you can: `fix(Button): the loading icon keeps spinning after loading ends`. + The scope is the component without its `B24` prefix — `Button`, not `B24Button` — + because that is what reaches the changelog. It is not required to file. + + **Issues here are public.** Please redact tokens, API keys, portal URLs and + customer data from anything you paste — logs, screenshots and reproductions + included. + + - type: input + id: component + attributes: + label: Component or composable + description: | + The name as it is exported — `B24Button`, `useOverlay`, `defineShortcuts`. The + `B24` prefix belongs here but not in the title; see above. + placeholder: B24Button + validations: + required: true + + - type: textarea + id: what-happens + attributes: + label: What happens + description: What you see, and what you expected instead. Screenshots help for anything visual. + validations: + required: true + + - type: textarea + id: reproduction + attributes: + label: Reproduction + description: | + A [StackBlitz](https://stackblitz.com/) or a small repository someone can open and see it in. + + This is the field that decides whether the report can be acted on. Most reports that + look like a bug in this library turn out to depend on something in the application + around it — a Tailwind config, a competing plugin, a wrapper component — and a + description alone cannot tell those apart from a real defect. + + If you genuinely cannot produce one, say so here and explain what blocks it. + A report with a reason is worth more than a report with an empty box. + placeholder: | + https://stackblitz.com/edit/... + + Or: what you tried, and what stopped you from isolating it. + validations: + required: true + + - type: input + id: version + attributes: + label: "`@bitrix24/b24ui-nuxt` version" + description: From your lockfile, not from memory — `pnpm why @bitrix24/b24ui-nuxt` prints it. + placeholder: 2.12.0 + validations: + required: true + + - type: dropdown + id: environment + attributes: + label: Environment + options: + - Nuxt + - Vue (without Nuxt) + - Both + validations: + required: true + + - type: textarea + id: versions + attributes: + label: Versions around it + description: | + Nuxt or Vue, Node, and your package manager. `npx nuxi info` prints the lot for a + Nuxt project — check its output before pasting, it includes local paths and your + full module list. + render: shell + validations: + required: false + + - type: textarea + id: extra + attributes: + label: Anything else + description: | + Whether it also reproduces in [Nuxt UI](https://ui.nuxt.com/) is worth a line if you + know — this is a fork, and a bug present upstream is fixed differently from one that + arrived in the port. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..dc465639 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,14 @@ +# Blank issues stay on: the maintainer files a lot of them, and an audit note or +# a follow-up does not fit either form. The forms are for reports arriving from +# outside, where the missing field is usually the reproduction. +blank_issues_enabled: true +contact_links: + - name: Documentation + url: https://bitrix24.github.io/b24ui/ + about: Component reference, theming, and the getting-started guides. + - name: Contributing + url: https://github.com/bitrix24/b24ui/blob/main/CONTRIBUTING.md + about: How to set the repository up, and where each guide lives. + - name: Bitrix24 REST API + url: https://apidocs.bitrix24.com/ + about: Questions about the Bitrix24 API itself rather than this UI library. diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 00000000..a32a69e3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,72 @@ +name: Feature request +description: Something Bitrix24 UI should be able to do and cannot. +title: "feat(Component): " +labels: [enhancement] +body: + - type: markdown + attributes: + value: | + Please check the [open issues](https://github.com/bitrix24/b24ui/issues?q=is%3Aissue+label%3Aenhancement) + first, and the [documentation](https://bitrix24.github.io/b24ui/) — a fair number of + requests turn out to be a prop that already exists under a different name. + + **Issues here are public.** Please redact tokens, API keys, portal URLs and + customer data from any code you paste. + + - type: textarea + id: problem + attributes: + label: What are you trying to do + description: | + The problem, not the solution. What are you building, and where does the library stop + being able to help? + + This is asked first on purpose. A request phrased as a solution can usually only be + accepted or rejected; a request phrased as a problem can be answered with something + better than what was asked for. + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: What you have in mind + description: The API you would want, if you have a shape in mind. Code is fine. + render: vue + validations: + required: false + + - type: dropdown + id: upstream + attributes: + label: Does this exist in Nuxt UI? + description: | + Bitrix24 UI is a fork of [Nuxt UI](https://github.com/nuxt/ui), ported deliberately + rather than merged wholesale. If the thing already exists upstream, the work is + "port and reconcile" instead of "design and build" — a much shorter conversation, + and knowing which one this is changes where it lands in the queue. + options: + - "Yes — it exists upstream" + - "No — this would be new" + - "I do not know" + validations: + required: true + + - type: input + id: upstream-link + attributes: + label: Link to it upstream + description: If you answered yes — the documentation page, the component, or the pull request. + placeholder: https://ui.nuxt.com/components/... + validations: + required: false + + - type: textarea + id: workaround + attributes: + label: What you are doing instead today + description: | + The workaround, if there is one. It tells us how much this hurts, and it is often the + clearest statement of the problem — including whether a fix is needed at all. + validations: + required: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..ecd4c035 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,92 @@ +# Contributing to Bitrix24 UI + +Thanks for being here. This file is deliberately short: it is a map, not a +guide. Everything it points at is written properly somewhere else, and this +page exists so GitHub can surface it — the "Contributing" link in the sidebar +and the banner on a new issue both look for a file with this name. + +## Start here + +| I want to… | Read | +|---|---| +| set up the repository and run it | [Contribution guide](https://bitrix24.github.io/b24ui/docs/getting-started/contribution/) | +| understand how the project is laid out | [`AGENTS.md`](AGENTS.md) | +| add or change a component | [Component structure](.github/contributing/component-structure.md) | +| change how something looks | [Theme structure](.github/contributing/theme-structure.md) | +| write or update tests | [Testing](.github/contributing/testing.md) | +| write documentation | [Documentation](.github/contributing/documentation.md) | +| understand how a release happens | [Releasing](.github/contributing/releasing.md) | + +`AGENTS.md` is written for AI coding agents and is picked up automatically by +most of them. It is also the fastest orientation for a human — project layout, +commands, conventions, in one file. + +## Reporting a bug + +[Open a bug report.](https://github.com/bitrix24/b24ui/issues/new?template=bug-report.yml) + +The form asks for a reproduction, and it asks because that is what decides +whether the report can be acted on. A component name and a description of what +looked wrong is usually not enough to tell a bug in this library from a bug in +the application using it — the shortest useful reproduction is a +[StackBlitz](https://stackblitz.com/) or a small repository someone can open +and see the problem in. + +If you are not sure whether it is a bug in this library or in the application +around it, open the report anyway and say so — a report that names the +uncertainty is more useful than a question that never gets asked. + +## Suggesting something + +[Open a feature request.](https://github.com/bitrix24/b24ui/issues/new?template=feature-request.yml) + +This library is a fork of [Nuxt UI](https://github.com/nuxt/ui), tracked and +ported deliberately rather than merged wholesale. If the thing you want exists +upstream, say so in the request — it changes the work from "design and build" +to "port and reconcile", which is a different and usually much shorter +conversation. + +## Opening a pull request + +Before you start on anything substantial, open an issue. It is not +bureaucracy: this repository carries divergences from upstream that are +recorded and intentional, and the fastest way to lose an afternoon is to fix +something that is deliberately the way it is. + +Then: + +- **Node** `^20.19.0 || >=22.12.0`, **pnpm** `11.20.0` (the version is pinned in + `package.json`; `corepack enable` picks it up). +- Run `pnpm run dev:prepare` after installing. Nothing typechecks before it. +- `pnpm run lint`, `pnpm run typecheck` and `pnpm run test run` all have to + pass locally. CI runs more than that: `pnpm run test:coverage` in place of + the plain run, so a large new area arriving untested is red — see + [Testing → Coverage](.github/contributing/testing.md#coverage) — plus + `pnpm test:module` and `pnpm build`, both of which can fail on a branch where + the three commands above are green. +- **The PR title is a [conventional commit](https://www.conventionalcommits.org/)** + and it is load-bearing. The title of the squashed commit is what generates the + changelog and decides the version bump, so a title that does not parse is + dropped from the release notes silently. A CI check enforces the format. +- Snapshots are regenerated with `pnpm run test:update` — the whole suite, never + a path. [Why.](.github/contributing/testing.md#snapshot-updates) + +The pull request template asks for a linked issue and a description. Both are +read. + +## Code of conduct + +There is no formal one yet. Be decent to each other in the meantime — the +usual reading applies: assume good faith, argue with the work rather than the +person, and take the hint when someone asks you to drop it. + +## Security + +**Please do not open a public issue for a vulnerability.** + +This repository does not yet publish a private channel to send one to, which +means there is currently nowhere good for a report to go — and a public issue +is the one place it should not go, because it discloses the problem to +everyone before there is a fix. If you have found something security-sensitive, +hold it until this section names a channel. That is the next thing to land +here. diff --git a/README.md b/README.md index 42e07a83..ec149156 100644 --- a/README.md +++ b/README.md @@ -104,8 +104,9 @@ Learn more in the [installation guide](https://bitrix24.github.io/b24ui/docs/get Thank you for considering contributing to Bitrix24 UI. Here are a few ways you can get involved: -- Reporting Bugs: If you come across any bugs or issues, please check out the reporting bugs guide to learn how to submit a bug report. -- Suggestions: Have any thoughts to enhance Bitrix24 UI? We'd love to hear them! Check out the [contribution guide](https://bitrix24.github.io/b24ui/docs/getting-started/contribution/) to share your suggestions. +- **Reporting bugs**: [open a bug report](https://github.com/bitrix24/b24ui/issues/new?template=bug-report.yml). The form asks for a reproduction, and that is the field that decides whether the report can be acted on — see [reporting a bug](https://github.com/bitrix24/b24ui/blob/main/CONTRIBUTING.md#reporting-a-bug). +- **Suggestions**: [open a feature request](https://github.com/bitrix24/b24ui/issues/new?template=feature-request.yml). Have any thoughts to enhance Bitrix24 UI? We'd love to hear them. +- **Code**: [CONTRIBUTING.md](https://github.com/bitrix24/b24ui/blob/main/CONTRIBUTING.md) is the map — setup, the deep-dive guides, and what CI expects of a pull request. The [contribution guide](https://bitrix24.github.io/b24ui/docs/getting-started/contribution/) on the documentation site covers getting the repository running. > [!TIP] > We provide contributing guidelines through [`AGENTS.md`](https://github.com/bitrix24/b24ui/blob/main/AGENTS.md) for AI assistants to help you contribute to Bitrix24 UI. It is automatically picked up by all AI coding agents and guides through component structure, theming patterns, testing conventions, and documentation guidelines. diff --git a/docs/content/docs/1.getting-started/4.contribution.md b/docs/content/docs/1.getting-started/4.contribution.md index 71da3b35..b081c889 100644 --- a/docs/content/docs/1.getting-started/4.contribution.md +++ b/docs/content/docs/1.getting-started/4.contribution.md @@ -8,7 +8,9 @@ The goal of this project is to provide components identical to Bitrix24 to enhan We have chosen [Nuxt UI](https://ui.nuxt.com/) as the foundation. We welcome your contributions in the form of bug reports, pull requests, and feedback to make this library even better. ::caution -Before reporting a bug or requesting a feature, make sure that you have read through our [documentation](https://bitrix24.github.io/b24ui/) and existing [issues](https://github.com/bitrix24/b24ui/issues?q=is%3Aissue%20is%3Aopen%20sort%3Aupdated-desc%20label%3Av3). +Before reporting a bug or requesting a feature, make sure that you have read through our [documentation](https://bitrix24.github.io/b24ui/) and the existing [issues](https://github.com/bitrix24/b24ui/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc). + +Then use a form: [bug report](https://github.com/bitrix24/b24ui/issues/new?template=bug-report.yml) or [feature request](https://github.com/bitrix24/b24ui/issues/new?template=feature-request.yml). The bug form asks for a reproduction, and that is the field that decides whether the report can be acted on. :: ## AI assistance @@ -70,7 +72,7 @@ The module code resides in the `src` folder. Here's a breakdown of its structure Before you start, check if there's an existing issue describing the problem or feature request you're working on. If there is, please leave a comment on the issue to let us know you're working on it. -If there isn't, open a new issue to discuss the problem or feature. +If there isn't, open a [feature request](https://github.com/bitrix24/b24ui/issues/new?template=feature-request.yml) to discuss the problem or feature. ### Local development diff --git a/package.json b/package.json index 6e93e033..723302b6 100644 --- a/package.json +++ b/package.json @@ -237,7 +237,8 @@ "vitest-axe": "^0.1.0", "vitest-environment-nuxt": "^2.0.0", "vue": "^3.5.41", - "vue-tsc": "^3.3.11" + "vue-tsc": "^3.3.11", + "yaml": "^2.9.0" }, "peerDependencies": { "@inertiajs/vue3": "^2.0.7 || ^3.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 575dbe48..7b465e57 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -311,6 +311,9 @@ importers: vue-tsc: specifier: ^3.3.11 version: 3.3.11(typescript@6.0.3) + yaml: + specifier: ^2.9.0 + version: 2.9.0 cli: dependencies: diff --git a/test/utils/documented-scripts.spec.ts b/test/utils/documented-scripts.spec.ts index 67175354..4c57cd61 100644 --- a/test/utils/documented-scripts.spec.ts +++ b/test/utils/documented-scripts.spec.ts @@ -16,11 +16,16 @@ import { describe, it, expect } from 'vitest' * `4.contribution.md` is the published mirror of the contributor guide. The * installation and usage pages are out, because there `pnpm add` and `pnpm dev` * belong to the reader's project, which this `package.json` says nothing about. + * + * `README.md` is out for the same reason: its `pnpm` lines install this package + * into somebody else's application. `CONTRIBUTING.md` is in — it is the first + * file GitHub shows a contributor, and every command in it runs here. */ const repoRoot = process.cwd() const DOC_FILES = [ 'AGENTS.md', + 'CONTRIBUTING.md', '.github/contributing/testing.md', '.github/contributing/documentation.md', '.github/contributing/component-structure.md', diff --git a/test/utils/governance-files.spec.ts b/test/utils/governance-files.spec.ts new file mode 100644 index 00000000..9b1a512b --- /dev/null +++ b/test/utils/governance-files.spec.ts @@ -0,0 +1,125 @@ +import { readdirSync, readFileSync } from 'node:fs' +import { join } from 'node:path' +import { describe, it, expect } from 'vitest' +import { parse } from 'yaml' + +const repoRoot = process.cwd() +const TEMPLATE_DIR = join(repoRoot, '.github/ISSUE_TEMPLATE') + +/** + * An issue form that does not match GitHub's schema does not fail loudly. The + * form silently stops being offered and the repository falls back to a blank + * issue box, which looks exactly like a repository that never had forms — so + * the first person to notice is whoever wonders why reports stopped carrying + * a reproduction. + * + * Nothing else here validates these files: eslint does not read the directory, + * and the two `.github` assert scripts look at `uses:` pins and install lines. + * This is the cheap half of the schema — the parts that actually go wrong when + * somebody edits a form by copying a block. + */ +const BLOCK_TYPES = new Set(['markdown', 'input', 'textarea', 'dropdown', 'checkboxes']) + +/** `render` takes a Linguist language name, lowercased. Only what the forms use. */ +const RENDER_LANGUAGES = new Set(['shell', 'vue', 'typescript', 'javascript', 'json', 'html', 'css']) + +/** + * Parsed, or the parse error carried as data. + * + * Throwing here would kill the whole file at collection time, and a spec that + * fails to collect reports as "no tests" — the same output as a spec nobody + * wrote. The error belongs in an assertion where it names the file. + */ +function forms(): [string, any, string | null][] { + return readdirSync(TEMPLATE_DIR) + .filter(name => name.endsWith('.yml') && name !== 'config.yml') + .sort() + .map((name) => { + try { + return [name, parse(readFileSync(join(TEMPLATE_DIR, name), 'utf8')), null] + } catch (error) { + return [name, null, String(error)] + } + }) +} + +describe('issue forms', () => { + const parsed = forms() + + it('are found at all', () => { + // The assertions below all pass on an empty list, which is how a directory + // that got renamed reads exactly like a directory that is fine. + expect(parsed.map(([name]) => name)).toEqual(['bug-report.yml', 'feature-request.yml']) + }) + + it.each(parsed)('%s matches the shape GitHub accepts', (name, form, parseError) => { + expect(parseError, `${name} is not valid YAML`).toBeNull() + expect(typeof form.name).toBe('string') + expect(typeof form.description).toBe('string') + expect(Array.isArray(form.body)).toBe(true) + + const ids: string[] = [] + + for (const block of form.body) { + expect(BLOCK_TYPES).toContain(block.type) + + if (block.type === 'markdown') { + // GitHub rejects a markdown block carrying `validations` or an `id`. + expect(block.validations).toBeUndefined() + expect(block.id).toBeUndefined() + expect(typeof block.attributes.value).toBe('string') + continue + } + + expect(block.id, `${block.type} block without an id`).toMatch(/^[a-z][a-z0-9-]*$/) + ids.push(block.id) + expect(typeof block.attributes.label).toBe('string') + + if (block.type === 'dropdown') { + expect(Array.isArray(block.attributes.options)).toBe(true) + expect(block.attributes.options.length).toBeGreaterThan(1) + // `None` is reserved on a required dropdown and silently breaks it. + if (block.validations?.required) expect(block.attributes.options).not.toContain('None') + } + + if (block.attributes.render !== undefined) { + expect(RENDER_LANGUAGES).toContain(block.attributes.render) + } + } + + expect(ids, 'duplicate ids').toEqual([...new Set(ids)]) + }) + + it('are reachable by the links that advertise them', () => { + // `?template=` names the file. A rename that misses one of these leaves a + // link that opens a blank issue instead of the form, which is not an error + // anywhere — the page just quietly shows the wrong thing. + const advertised = ['README.md', 'CONTRIBUTING.md', 'docs/content/docs/1.getting-started/4.contribution.md'] + .flatMap(file => [...readFileSync(join(repoRoot, file), 'utf8').matchAll(/\?template=([\w.-]+)/g)]) + .map(match => match[1]!) + + expect(advertised.length).toBeGreaterThan(0) + expect([...new Set(advertised)].sort()).toEqual(parsed.map(([name]) => name)) + }) +}) + +/** + * `CONTRIBUTING.md` quotes the pinned Node and pnpm versions as prose. That is + * the right place for them — it is the first thing a contributor reads — but + * prose does not run, and `documented-scripts.spec.ts` only checks script + * names, so a `packageManager` bump would leave the doc wrong with nothing + * red anywhere. + */ +describe('CONTRIBUTING.md', () => { + const contributing = readFileSync(join(repoRoot, 'CONTRIBUTING.md'), 'utf8') + const pkg = JSON.parse(readFileSync(join(repoRoot, 'package.json'), 'utf8')) + + it('quotes the pinned pnpm version', () => { + const pinned = pkg.packageManager.replace(/^pnpm@/, '') + expect(contributing, `package.json pins pnpm@${pinned}`).toContain(pinned) + }) + + it('quotes the supported Node range', () => { + expect(contributing, `package.json declares engines.node ${pkg.engines.node}`).toContain(pkg.engines.node) + }) +})