Skip to content

Commit 0ba0bc1

Browse files
committed
fix(#6250 round-7): anchor Inputs role attribute predicate to full parsed token
PerishCode's round-7 review pointed out that the Inputs group's role attribute matcher still bypassed the boundary the rest of the head establishes for other component groups: it matched attribute values containing 'role=checkbox' text inside unrelated attributes like [data-label="[role=checkbox]"] and did not reject :not([role="checkbox"]) properly. Fix: anchor the regex with ^...\]/i (same pattern that landed on Buttons and Icons in round-6) so only complete parsed attribute tokens with a role predicate are admitted to Inputs. Add regression tests for three cases: - :not([role="checkbox"]) is not admitted to Inputs - [data-label="[role=checkbox]"] is not admitted to Inputs - positive control: [role="checkbox"] still admits to Inputs Validation: pnpm exec vitest run packages/contracts/tests/components-manifest-6250-not-and-attribute-opacity.test.ts -> 14 passed (was 11 before, +3 new) pnpm exec vitest run (full contracts suite) -> 298 passed (44 files) pnpm exec tsc -p tsconfig.json --noEmit -> clean Signed-off-by: xxiaoxiong <2482929840@qq.com>
1 parent 56bf191 commit 0ba0bc1

2 files changed

Lines changed: 32 additions & 4 deletions

File tree

packages/contracts/src/design-systems/components-manifest.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,11 @@ const COMPONENT_GROUPS: ComponentGroupDefinition[] = [
122122
attributeMatchers: [
123123
// Genuine inputs attribute predicates (round-5 follow-up): `[role=checkbox]`
124124
// and `[role=radio]` describe inputs components via attribute rather than
125-
// element name, and they appear verbatim in the compound; tokenizeCompound
126-
// would lose the attribute value (only the bracket text is skipped), so
127-
// we run them against the raw selector.
128-
/\[role=["']?(?:checkbox|radio|textbox|search|spinbutton)["']?/i,
125+
// element name. Anchor to the complete parsed attribute token with ^ and
126+
// trailing ] so an unrelated attribute whose value contains role text
127+
// (e.g. `[data-label="[role=checkbox]"]`) is not admitted to Inputs —
128+
// same round-6 fix that landed on Buttons and Icons.
129+
/^\[role=["']?(?:checkbox|radio|textbox|search|spinbutton)["']?\]/i,
129130
],
130131
// `^form(?:$|-)` was too permissive once class tokens were matched per-token
131132
// (PerishCode round-3 follow-up #6250): it admitted `.form-input-prepend`

packages/contracts/tests/components-manifest-6250-not-and-attribute-opacity.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,12 @@ describe('#6250 round-5 — :not() and attribute-value opacity', () => {
5757
:not([type="submit"]) { color: var(--tone-button-not-attr) }
5858
:not([aria-hidden="true"]) { color: var(--tone-icon-not-attr) }
5959
[data-label="[type=submit]"] { color: var(--tone-button-attr-leak) }
60+
[role="checkbox"] { color: var(--tone-input-role-attr) }
61+
:not([role="checkbox"]) { color: var(--tone-input-role-not-attr) }
62+
[data-label="[role=checkbox]"] { color: var(--tone-input-role-attr-leak) }
6063
</style>
6164
<button>real button</button>
65+
<input type="checkbox" />
6266
`;
6367

6468
const manifest = extractComponentsManifest({
@@ -138,4 +142,27 @@ describe('#6250 round-5 — :not() and attribute-value opacity', () => {
138142
expect(icons).toBeDefined();
139143
expect(icons!.tokenReferences.some((r) => r === '--tone-icon')).toBe(true);
140144
});
145+
146+
// ----- Round-7: Inputs role attribute predicate opacity (PerishCode round-7 blocker) -----
147+
148+
it(':not([role="checkbox"]) is NOT admitted to Inputs — :not() erases attribute too', () => {
149+
const inputs = findGroup(manifest, 'inputs');
150+
expect(inputs).toBeDefined();
151+
expect(inputs!.selectors.some((s) => s.includes(':not([role="checkbox"])'))).toBe(false);
152+
expect(inputs!.tokenReferences.some((r) => r === 'tone-input-role-not-attr' || r === '--tone-input-role-not-attr')).toBe(false);
153+
});
154+
155+
it('[data-label="[role=checkbox]"] is NOT admitted to Inputs — value text is not a real predicate', () => {
156+
const inputs = findGroup(manifest, 'inputs');
157+
expect(inputs).toBeDefined();
158+
expect(inputs!.selectors.some((s) => s.includes('[data-label="[role=checkbox]"]'))).toBe(false);
159+
expect(inputs!.tokenReferences.some((r) => r === 'tone-input-role-attr-leak' || r === '--tone-input-role-attr-leak')).toBe(false);
160+
});
161+
162+
it('positive control: [role="checkbox"] still admits to Inputs and contributes tokenReferences', () => {
163+
const inputs = findGroup(manifest, 'inputs');
164+
expect(inputs).toBeDefined();
165+
expect(inputs!.selectors.some((s) => s.includes('[role="checkbox"]'))).toBe(true);
166+
expect(inputs!.tokenReferences.some((r) => r === '--tone-input-role-attr')).toBe(true);
167+
});
141168
});

0 commit comments

Comments
 (0)